同学,你知道super关键字去哪儿了吗

144 阅读3分钟

super 关键字的一点思考


class A {}
class B extends A {
    constructor() {
        super() // KEY
    }
}

let b = new B();  // this?  prototype呢

我们今天从一个简单的面试题出发,开始逐步去认识 super 是如何去使用的?

这时我们提出几个简单的问题:

  • b实际上指向的是谁?
  • A 和 B 之间存在着怎样的关系?
  • super 到底做了什么事情呢?

开始正式步入正题

在实际使用super的时候 会分为函数调用和对象调用

第一种情况 super 作为函数调用

super 作为函数调用的时候,代表父类的构造函数. es6要求, 子类的构造函数必须执行一次super函数

class A {}

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

上面代码中, 子类B的构造函数中的 super(), 代表调用父类的构造函数. 当然这是规范所要求的, 不然会报错

此时我们需要注意的是, 虽然 super 代表了父类A的构造函数, 但是返回的是子类B的实例。即 super 内部的 this指的是 B的实例.

因此 super() 这里相当于是 A.prototype.constructor.call(this).

下面我们再来看一段代码

class A {
  constructor() {
    console.log(new.target.name)
  }
}

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

new A() // A
new B() // B

上面的代码中, new.target 指向当前正在执行的函数,我们可以看到,当 super()执行时,它的指向是什么呢? 指向的是 子类B 的构造函数,可能我们会想到是父类A的构造函数, 也就是说, super() 指向的是 B.

第二种情况 super 作为对象调用

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

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

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

let b = new B();

从上面的代码中,我们可以看出在 子类 B 当中的 super.p() 当作一个对象使用. 这时候呢, super在普通方法中, 指向的是 A的 prototype, 所以 super.p() 就相当于 A.prototype.p()

这里需要注意几点

  1. 由于 super指向父类的原型对象, 所以定义在父类实例上的方法或者属性是无法通过super调用的
  2. 但是 如果是定义在父类的原型上的话, super就可以取到的
class A {}
A.prototype.child = 'jiuwo';

class B extends A {
  constructor() {
    super();
    console.log(super.child) // 'jiuwo'
  }
}
let b = new B()

上面的代码中, 属性 child 是定义在 A.prototype , 所以呢 我们可以通过 super.child 取到它的值。

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

class A {
  constructor() {
    this.x = 1;
  }
  get() {
    console.log(this.x)
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  child() {
    super.get();
  }
}

let b = new B();
b.child() // 2

上面的代码, 我们可以看到 super.child() 虽然调用的是 A.prototype.get(),但是实际上呢, A.prototype.get() 内部调用的是子类B的实例, 所以最后导致的结果就是 2,而不是 1. 也就是说呢, 实际上执行的是, super.get.call(this).

由于 this会指向子类的实例这个特性, 所以呢 当我们通过 super 对某个属性赋值的时候, 这时的 super 就是 this, 赋值的属性会变成子类实例的属性。

所以到这里 我们的学习似乎暂时要结束了,你是否能解答出文章的开头几个问题呢?