美文网首页
JavaScript之Class (类)

JavaScript之Class (类)

作者: 又菜又爱分享的小肖 | 来源:发表于2021-06-30 21:45 被阅读0次

    面向对象的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但是JavaScript中并没有类的概念,所以它的对象也与基于类的语言中的对象有所不同。
    JavaScript中把对象定义为:无序属性的集合,其属性可以包含值,对象或者函数。
    虽然Objeck构造函数或对象字面量都可以创建对象,但这些方法有个很明显的特点:使用一个接口创建很多对象,会产生大量的重复代码。

    JavaScript语言的传统方法是通过构造函数定义并生成新对象。

            function Point(x, y){
                this.x = x;
                this.y = y;
            }
            Point.prototype.toString = function(){
                return `${this.x} + ${this.y}`
            }
            var p = new Point(1,2).toString();
            console.log(p);
    

    prototype 这个属性是一个指针,指向一个对象的原型。

    new在里面做了四个操作:

    1. 创建一个新对象
    2. 将构造函数的作用域赋给这个新对象(所以this指向这个新对象)
    3. 执行构造函数中的代码(为了这个新对象添加属性)
    4. 返回一个新对象

    ES6中提供了更接近传统语言的写法,引入了Class(类)这个概念作为对象的模板。通过class关键字可以定义类。

            class Point{
                constructor(x, y){
                    this.x = x;
                    this.y = y;
                }
                toString(){
                    return `${this.x + this.y}`
                }
            }
           var s = new Point(1,2);
           console.log(s);
    

    上面的代码定义了一个类,可以看到里面有个constructor方法,这就是构造函数,而this关键字则代表实例对象。
    Point 类除了构造函数,还定义了一个toString方法。

    定义类的方法时,前面不需要加上function这个保留字,直接把函数定义放进去就可以了。另外,方法之间不需要逗号分隔,加了会报错。

            class Point{};
            console.log(typeof Point); //"function"
            console.log(Point === Point.prototype.constructor); //true
    

    类的数据类型就是函数,类本身就指向构造函数。

            class Point{
                constructor(){
    
                }
                toValue(){
                    
                }
            }
    
            //等同于
            Point.prototype = {
                constructor(){},
                toValue(){}
            }
    

    在类里面调用方法,其实就是调用原型上的方法。

            class B {}
            let b = new B();
            B.constructor === b.prototype.constructor // true
    

    上面的代码中,b是B类的实例,它的constructor 方法就是B类原型的constructor 方法。
    constructor始终指向创建当前对象的构造函数。
    prototype 就是通过调用构造函数而创建的那个对象实例的原型对象。
    prototype 对象的constructor 属性直接指向类本身。

    类的内部定义的所有方法都是不可枚举的

            class Point {
                constructor(){
    
                }
                toString(){
    
                }
            }
            Object.keys(Point.prototype); // []
            Object.getOwnPropertyNames(Point.prototype); //["constructor", ["toString"]
    

    类的属性名可以使用表达式

            let methodName = 'getArea';
            class Point {
                constructor (){
    
                }
                [methodName] (){
                    
                }
            }
    

    constructor 方法

    constructor 方法是类的默认方法,通过new生成对象实例时自动调用该方法。一个类必须有constructor 方法,如果没有显式定义,一个空的constructor 方法会被默认添加。

            function Point(x, y){
                this.x = x;
                this.y = y;
            }
            Point.prototype.toString = function(){
                return `${this.x} + ${this.y}`
            }
            var p = new Point(1,2);
            console.log(p.hasOwnProperty('x')); //true
            console.log(p.hasOwnProperty('y')); //true
            console.log(p.hasOwnProperty('toString')) //false
            console.log(p.__proto__.hasOwnProperty('toString')) //true
    

    x和y都是实例对象 p 自身的属性(因为定义在this变量上),所以hasOwnProperty方法返回为true,而toString方法是原型对象的属性(因为定义在Point类上),所以hasOwnProperty方法返回为false。
    与ES5一样,类的所以实例共享一个原型对象。

            var p1 = new Point(1,2);
            var p2 = new Point(2.3);
            p1.__proto__ === p2.__proto__; //true
    

    proto并不是语言本身的特性,而是各大厂商具体实现时添加的私有属性,虽然目前很多现代浏览器的js引擎中都提供了这个私有属性,但依旧不建议再生产中使用该属性,避免对环境产生依赖。生产环境中,可以使用Objeck.getPrototypeOf 方法来获取实例对象的原型,然后在来为原型添加方法&属性。

    私有方法

    一种做法是在命名上加以区别

            class Widget {
                //公有方法
                foo(baz){
                    this._bar(baz);
                }
    
                //私有方法
                _bar(baz){
                    return this.snaf = baz;
                }
            }
    

    _bar方法前面的下划线表示这是一个只限于内部使用的私有方法。但是,这种命名是不保险的,在类的外部依然可以调用这个方法。
    另一种方法是索性将私有方法移出模块,因为模板内部的所有方法都是对外可见的。

            class Widget {
                //公有方法
                foo(baz){
                    bar.call(this, baz);
                }
            }
            function bar(baz){
                return this.snaf = baz;
            }
    

    foo是公有方法,内部调用了bar.call(this, baz)。这使得bar实际上成为了当前模块的私有方法。

    还有一种方法,利用Symbol值的唯一性将私有方法的名字命名为一个symbol值。

            const bar = Symbol('bar');
            const snaf = Symbol('snaf');
            class Widget {
                //公有方法
                foo(baz){
                    this[bar](baz);
                }
                [bar](baz){
                    return this[snaf] = baz;
                }
            }
    

    利用symbol值的唯一性,导致第三方无法获取到它,因此达到了私有方法和私有属性的效果。

    私有属性

    为class加了私有属性。方法是在属性名之前,使用#来表示。

            class Point{
                #x;
                constructor(x = 0){
                    #x = +x;
                }
                get x() {
                    return #x;
                }
                set x(value){
                    #x = +value;
                }
            }
    

    .#x表示私有属性x,在Point类之外是读取不到这个属性的,私有属性与实例的属性是可以同名的

    this的指向

    类的方法内部如果含有 this,它将默认指向类的实例。但是,一旦单独使用该方法,很可能或报错。

    相关文章

      网友评论

          本文标题:JavaScript之Class (类)

          本文链接:https://www.haomeiwen.com/subject/gibkultx.html