简介

Class 可以通过 extends 关键字实现继承,比Es5通过修改原型链实现继承,更清晰和方便。

  1. class Point {}
  2. class ColorPoint extends Point {}

ColorPoint 通过extends 继承了 point 的所有方法和属性。两个类一样。

  1. class ColorPoint extends Point {
  2. constructor(x, y, color) {
  3. super(x, y)
  4. this.color = color
  5. }
  6. toString() {
  7. return this.color + ' ' + super.toString() // 调用父类的toString()
  8. }
  9. }

super 关键字 表示父类的构造函数,用来新建父类的 this 对象。

子类必须在contructor 方法中调用super 方法,否则新建实例时会报错。
子类的this 对象,必须先通过父类的构造函数完成塑造,得到父类同样的实例属性和方法,然后再对其加上自己的实例属性和方法。如果不调用super 方法,子类就不能得到this 对象。

  1. class Point {}
  2. class ColorPoint extends Point {
  3. constructor() {}
  4. }
  5. let cp = new ColorPoint() // 报错

ColorPoint 继承了父类Point, 但它的构造函数没有调用 super 方法,导致新建实例时报错。

Es6 继承机制:
先将父类实例对象的属性和方法,加到this 上面(必须先调用super方法),然后再用子类的构造函数修改this.

如果子类没有定义constructor 方法,这个方法会被默认添加。

  1. class ColorPoint extends Point {}
  2. // 等同于
  3. class ColorPoint exntends Point {
  4. construcotr(...args) {
  5. super(...args)
  6. }
  7. }

注意: 在子类的构造函数中,只有调用super 方法后,才可以使用 this 关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super 方法才能调用父类实例。

  1. class Point {
  2. constructor(x, y) {
  3. this.x = x
  4. this.y = y
  5. }
  6. }
  7. class ColorPoint extends Point {
  8. constructor(x, y, color) {
  9. this.color = color // 报错
  10. super(x, y)
  11. this.color = color // 正确
  12. }
  13. }

子类的constructor 方法this 关键字,必须要在super() 调用后使用。

  1. let cp = new ColorPoint(25, 8, 'green')
  2. cp instanceof ColorPoint // true
  3. cp instanceof Point // true

cp 同时是ColorPoint和Point 两个类的实例。

父类的静态方法也会被子类继承

  1. class A {
  2. static hello() {
  3. console.log('hello world')
  4. }
  5. }
  6. class B extends A {}
  7. B.hello() // 'hello world'

Object.getPrototypeOf()

Object.getPrototypeOf 方法可从子类获取父类

  1. Object.getPrototypeOf(ColorPoint) === point // true

用这个方法判断: 一个类是否继承了另一个类。

super 关键字

可以当函数使用,也可以当对象使用。

super =》函数
super 方法,代表父类的构造函数。Es6 要求,子类的构造函数必须执行一次super函数。

  1. class A {}
  2. class B extends A {
  3. construcor() {
  4. super()
  5. }
  6. }

super 虽然代表了父类A 的构造函数,但返回的是子类B的实例,即super 内部的 this 值的是B的实例。
super() => A.prototype.constructor.call(this)

  1. class A {
  2. constructor() {
  3. console.log(new.target.name)
  4. }
  5. }
  6. class B extends A {
  7. constructor() {
  8. super()
  9. }
  10. }
  11. new A() // A
  12. new B() // B

new.target 指向当前正执行的函数。所以,supe() 执行时,指向的是子类B的构造函数,而不是父类A的构造函数。

作为函数时,super() 只能用在子类的构造函数中,用在其它地方会报错。

  1. class A {}
  2. class B extens A {
  3. m() {
  4. super() // 报错
  5. }
  6. }

super() 用在B类的m方法中,就会导致语句报错。

super 作为对象

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

  1. class A {
  2. p() {
  3. return 2
  4. }
  5. }
  6. class B extens A {
  7. constructor() {
  8. super()
  9. console.log(super.p()); // 2
  10. }
  11. }
  12. let b = new B()

子类B 中普通方法的 super,指向父类A.prototype, 所以 super.p() 相当于A.prototype.p()

由于super 指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super 调用的。

  1. calss A {
  2. constructor() {
  3. this.p = 2
  4. }
  5. }
  6. class B extends A {
  7. get m() {
  8. return super.p;
  9. }
  10. }
  11. let b = new B()
  12. b.m // undefined

p 是父类A实例的属性, super.p 就引用不到它。

如果属性定义在父类的原型对象上,super 就可以取到。

  1. calss A {}
  2. A.prototype.x = 2
  3. class B extends A {
  4. get m() {
  5. return super.x;
  6. }
  7. }
  8. let b = new B()
  9. b.m // 2

属性x 定义在A.prototype 上, 所以 super.x 可以取到它的值。

Es6规定,在子类的普通方法中通过super 调用父类的方法时,方法内部的this指向当前的子类实例。

  1. class A {
  2. constructor() {
  3. this.x = 1
  4. }
  5. print() {
  6. console.log(this.x)
  7. }
  8. }
  9. class B extends A {
  10. constructor() {
  11. super()
  12. this.x = 2
  13. }
  14. m() {
  15. super.print()
  16. }
  17. }
  18. let b = new B()
  19. b.m() // 2

super.print()虽然调用A.prototype.print(), 但是A.portotype.print() 内部的this 指向子类的B实例,导致输出的时2,而不是1.实际上执行的是 super.print.call(this)

由于this 指向子类实例,所以如果 super() 对某个属性赋值,这时super 就是 this, 赋值的属性会变成子类实例属性。

  1. clas A {
  2. constructor() {
  3. this.x = 1
  4. }
  5. }
  6. class B extens A {
  7. constructor() {
  8. super()
  9. this.x = 2
  10. super.x = 3
  11. console.log(super.x) // undefined
  12. console.log(this.x) // 3
  13. }
  14. }
  15. let b = new B()

supper.x = 3, 等同于 this.x = 3. 当读取super.x 时,读取的是 A.prototype.x, 因为 A 的原型上没有x 属性,返回undefined.

如果super 作为对象,用在静态方法中,这时 super 指向父类,而不是父类的原型对象。

  1. class Parent {
  2. static myMethod(msg) {
  3. console.log('static', msg)
  4. }
  5. myMethod(msg) {
  6. console.log('instance', msg)
  7. }
  8. }
  9. class Child extends Parent {
  10. static myMethod(msg) {
  11. supe.myMethod(msg)
  12. }
  13. myMethod(msg) {
  14. super.myMethod(msg)
  15. }
  16. }
  17. Child.myMethod(1) // static 1
  18. let child = new Child()
  19. child.myMethod(2) // instance 2

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

在子类的静态方法中通过super调用父类的方法时,方法内部的this 指向当前子类,而不是子类的实例。

  1. class A {
  2. constructor() {
  3. this.x = 1;
  4. }
  5. static print() {
  6. console.log(this.x)
  7. }
  8. }
  9. class B extends A {
  10. constructor() {
  11. super()
  12. this.x = 2
  13. }
  14. static m() {
  15. super.print()
  16. }
  17. }
  18. B.x = 3
  19. B.m() // 3

静态方法 B.m 中, super.print 指向父类的静态方法。这个方法里 this 指向的是 B,而不是B的实例。

注意: 使用super 时, 必须显示指定是作为函数、还是作为对象使用,否则会报错。

  1. class A {}
  2. class B extends A {
  3. constructor() {
  4. super()
  5. console.log(super); // 报错
  6. console.log(super.valueof() instanceof B); // true
  7. }
  8. }
  9. let b = new B()

console.log(super)中的super,无法看出是作为函数使用,还是作为对象使用,会报错。
super.valueOf() 表明super是一个对象,因此不会报错。由于 super使得this指向B的实例,所以super.valueOf() 返回的是一个B的实例。

由于对象总是继承其它对象的,所以可以在任意一个对象中,使用super 关键字。

  1. let obj = {
  2. toString() {
  3. return 'MyObject:' + super.toString()
  4. }
  5. }
  6. obj.toString(); // MyObject: [Object Object]

类的 prototype 属性和 proto 属性

Class 同时有 prototype 属性和 proto属性,因此同时存在两条继承链。
子类的proto 属性,表示构造函数的继承,总是指向父类。
子类prototype属性的proto 属性,表示方法的继承,总是指向父类的prototype 属性。

  1. class A {}
  2. class B extends A {}
  3. B.__proto__ === A; // true
  4. B.prototoype.__proto__ === A.prototype

类的继承是按照下面的模式实现的。

  1. class A {}
  2. class B {}
  3. // B 的实例继承 A的实例
  4. Object.setPrototypeOf(B.prototype, A.prototype)
  5. // B 继承 A 的静态属性
  6. Object.setPrototype(B, A)

Object.setPrototypeOf 方法的实现

  1. Object.setPrototypeOf = function(obj, proto) {
  2. obj.__proto__ = proto
  3. return obj
  4. }

原生构造函数的继承