super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
1、super作为函数调用
super作为
函数
调用时,代表
父类的构造函数
。ES6 要求,子类的构造函数必须执行一次super函数。
class A {}
class B extends A {
constructor() {
super();
}
}
上面代码中,子类B的构造函数之中的super(),代表调用父类的构造函数。这是必须的,否则 JavaScript 引擎会报错。
2、super作为对象
super作为
对象
时,在
普通方法
中,指向父类的原型对象;在
静态方法
中,指向父类。
为什么不同的方法中super的指向不同呢?ES6中定义super相当于指向对象原型的指针,即super=>Object.getPrototypeOf(this)。
在来看一下方法的定义:具有功能而非数据的对象属性,内部有[[HomeObject]]属性来包容这个方法从属的对象。
所以在方法中使用super时,super的引用即是通过[[HomeObject]]来确定的:
- 在[[HomeObject]]上调用Object.getPrototypeOf()找到原型,super指向该原型;
- 在该原型上找到同名方法;
- 绑定this;
下面来看两个例子:
例1:普通方法
class A {
p() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.p()); // 2
}
}
let b = new B();
上面代码中,子类B当中的super.p()在constructor方法中,constructor的[[HomeObject]]为B.prototype,所以super=>Object.getPrototypeOf(B.prototype)=>A.prototype,即super指向A.prototype,所以super.p()就相当于A.prototype.p()。
这里需要注意,由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。
下面看第二个例子:
例2:静态方法
class Parent {
static myMethod(msg) {
console.log('static', msg);
}
myMethod(msg) {
console.log('instance', msg);
}
}
class Child extends Parent {
static myMethod(msg) {
super.myMethod(msg);
}
myMethod(msg) {
super.myMethod(msg);
}
}
Child.myMethod(1); // static 1
var child = new Child();
child.myMethod(2); // instance 2
在静态方法之中,静态方法的[[HomeObject]]为B,所以super=>Object.getPrototypeOf(B)=>B.prototype=>A.instance,即super指向A的实例,所以这时super将指向父类A,而不是父类的原型对象。