es6 class

96 阅读4分钟

关于类

  • 类的数据类型就是函数,类本身就指向构造函数
class Point {
  // ...
}

typeof Point // "function"
Point === Point.prototype.constructor // true
  • 构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。
class Point {
  constructor() {
    // ...
  }

  toString() {
    // ...
  }

  toValue() {
    // ...
  }
}

// 等同于

Point.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
};
  • 因此,在类的实例上面调用方法,其实就是调用原型上的方法。
class B {}
const b = new B();

b.constructor === B.prototype.constructor

prototype对象的constructor()属性,直接指向“类”的本身,这与 ES5 的行为是一致的。

Point.prototype.constructor === Point // true
  • 类的内部所有定义的方法,都是不可枚举的
Object.keys(Point.prototype) 
// []

构造函数 constructor

  • constructor()方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。
  • 一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加。
  • constructor()方法默认返回实例对象(即this
类跟普通构造函数的一个主要区别
  • 类必须使用new调用,否则会报错
  • 普通构造函数可以不用new也可以执行

在一个构造方法中可以使用super关键字来调用一个父类的构造方法

class Square extends Polygon {
    constructor(length) {
        // 在这里, 它调用了父类的构造函数, 并将 lengths 提供给 Polygon 的"width"和"height"
        super(length, length);
        // 注意: 在派生类中, 必须先调用 super() 才能使用 "this"。
        // 忽略这个,将会导致一个引用错误。
        this.name = 'Square';
    }
    get area() {
        return this.height * this.width;
    }
    set area(value) {
        // 注意:不可使用 this.area = value
        // 否则会导致循环call setter方法导致爆栈
        this._area = value;
    }
}

类的实例化

类的所有实例共享一个原型对象。

class B {
    constructor(a, b)
    this.a = a
    this.b = b
}
const b = new B(2,1);
const c = new B(3,1);
b._proto_ == c._proto_  // true

可以通过实例的__proto__属性为“类”添加方法

  • 当通过一个实例给类的prototype添加方法的时候,别的实例也可以调用这个新添加的方法
  • 使用实例的__proto__属性改写原型,必须相当谨慎,不推荐使用,因为这会改变“类”的原始定义,影响到所有实例。
  • 而且__proto__不是语言本身的特性,谨慎使用

静态方法 static

类(class)通过 static 关键字定义静态方法。不能在类的实例上调用静态方法,而应该通过类本身调用。这些通常是实用程序方法,例如创建或克隆对象的功能。

  • 注意,如果静态方法包含this关键字,这个this指的是类,而不是实例
  • 父类的静态方法,可以被子类继承。子类也可以调用
  • 静态方法也是可以从super对象上调用的
    class Foo {
      static classMethod() {
        return 'hello';
      }
    }

    class Bar extends Foo {
      static classMethod() {
        return super.classMethod() + ', too';
      }
    }

    Bar.classMethod() // "hello, too"

关键字 super

super关键字用于访问和调用一个对象的父对象上的函数。

在构造函数中使用时,super关键字将单独出现,并且必须在使用this关键字之前使用。super关键字也可以用来调用父对象上的函数

  • 调用父类的构造函数
  • 调用父类上的静态方法
  • 子类 constructor 方法中必须有 super ,且必须出现在 this 之前。
  • 调用父类构造函数,只能出现在子类的构造函数。
        class Child extends Father {
            constructor() {
                super(); // super() 只能出现在子类的 constructor 中
            }
        }
    
  • 调用父类方法, super 作为对象,在普通方法中,指向父类的原型对象,在静态方法中,指向父类。
        class Father {
            test() {
                return 0;
            }
            static test1() {
                return 1;
            }
        }
        
        class Child2 extends Father {
            constructor(){
                super();
                // 调用父类普通方法
                console.log(super.test()); // 0
            }
            static test3(){
                // 调用父类静态方法  只有类才能调用 静态方法
                return super.test1+2;
            }
        }
        Child2.test3(); // 3
    
    
    

继承 extends

子类通过extends继承父类时,必须在constructor方法中调用super方法,否则子类通过new关键字新建实例时会报错。
这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,
然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。

+ ES5 的继承,实质是先创造子类的实例对象`this`,然后再将父类的方法添加到`this`上面(`Parent.apply(this)`)。
+ ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到`this`上面(所以必须先调用`super`方法),然后再用子类的构造函数修改`this`。
+ 父类的静态方法,也会被子类继承。

class 不存在变量提升

new Foo(); // ReferenceError
class Foo {}

因为不存在变量提升,所以 使用 new关键字的时候,Foo还没有定义,所以抛错

文章摘自ES6新特性 Class 类的全方面理解