1.简介

Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多

class Point {

}

class ColorPoint extends Point {

}

上面代码,ColorPoint 继承了 Point,因为没有部署代码,所以两个类是完全一样的。

子类必须在 constructor 中调用 super 方法,否则会出错。因为子类没有自己的 this 对象,所以需要 super 方法继承父类的 this 对象。

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y)

    this.color = color;
  }
}

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

作为子类的默认 constructor 方法

class ColorPoint extends Point {
}
class ColorPoint extends Point {
  constructor(...args) {
    super(...args)
}

下面代码中,实例对象cp同时是ColorPoint和Point两个类的实例,这与 ES5 的行为完全一致。

let cp = new ColorPoint(25, 8, 'green');

cp instanceof ColorPoint // true
cp instanceof Point // true

最后,父类的静态方法,也会被子类继承。

2.Object.getPrototypeOf()

Object.getPrototypeOf(ColorPoint) === Point // true

3.super 关键字

super 关键字可以作为函数和对象使用。

作为函数时,代表父类的构造函数,用于在子类的构造函数中创建子类的实例对象 this

super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B,因此super()在这里相当于 A.prototype.constructor.call(this)

并且作为函数时,只能放在子类的构造函数中。

class A {}

class B extends A {
  constructor() {
    super();
  }
}

作为对象时,在普通方法中,指向父类的原型对象,在静态方法中,指向父类。

class A {
  p() {
    return 2;
  }
}

class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2
  }
}

let b = new B();

因为这时 super 指向的是父类的原型对象,所以定义在父类实例上的方法和属性是访问不到的。

class A {
  constructor() {
    this.p = 2;
  }
}

class B extends A {
  get m() {
    return super.p;
  }
}

let b = new B();
b.m // undefined

根据上面的特性就有下面的例子

class A {
  constructor() {
    this.x = 1
  }
}

class B extends A {
  constructor() {
    super()

    this.x = 2
    console.log(super.x) // undefined
    super.x = 3
    console.log(this.x) // 3
    console.log(super.x) // undefined
  }
}

new B()

作为对象用在静态方法中,super 将指向父类。

class A {
  say() {
    console.log('hello')
  }
  static say() {
    console.log('world')
  }
}

class B extends A {
  say() {
    super.say()
  }
  static say() {
    super.say()
  }
}

super 在使用的时候必须显示的指定是作为函数还是作为对象使用的,否则将会报错。

class A {}

class B extends A {
  constructor() {
    super();
    console.log(super); // 报错
  }
}

4.类的 prototype 属性和 proto 属性

大多数浏览器的 ES5 实现之中,每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。Class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。

  • extends 的继承目标

  • 实例的 proto 属性

5.原生构造函数的继承

一个继承 Array 的例子

class MyArray extends Array {
  constructor() {
    super(arguments)
  }
}

6.Mixin 模式的实现