前言
ES6 中的继承和 super 的用法大家都不会陌生,可是一问到 super 到底是什么,估计很对人都会回答不上来。在 ES6 中,super 是一个特殊的语法,而且它比 this 还要特殊,有很多用法上的限制。
一、super是什么?
MDN: super关键字用于访问和调用一个对象的父对象上的函数
二、super怎么用?
1.当做函数使用
super
作为函数调用时,代表父类的构造函数。ES6要求,class继承中子类的构造函数必须执行一次super
函数,否则JavaScript引擎会报错。
代码如下(示例):
class A {}
class B extends A {
constructor() {
super();
}
}
1.super()返回的是子类的实例
super虽然代表了父类A的构造函数,但返回的是子类B的实例,即super内部的this指的是B的实例,因此super()在这里相当于A.prototype.constructor.call(this)
。
代码如下(示例):
var a=null
var b=null
class A{
constructor(){
console.log(new.target.name) //new.target指向正在执行的函数
}
}
class B extends A{
constructor(){
a = super()
}
}
b=new B //B
console.log(b===a)//true
可以看到当执行super()
的时候,new.target.name返回B
表明执行的是函数B而不是函数A,这说明在子类继承父类时super()的this
指向子类。
b===a返回true
,表明了super()
虽然代表了父类A的构造函数,但返回的是子类B的实例。
2.super()只能在子类的构造函数中调用
作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错。
class A {}
class B extends A {
m() {
super(); // 报错
}
}
2.当做对象使用
1.在普通方法中,super指向父类的原型对象
class A {
p(){
console.log('A.p')
}
}
class B extends A {
constructor() {
super();
}
p(){
console.log('B.p')
}
getP(){
super.p()
console.log(super.p===A.prototype.p)//true
}
}
var b=new B
b.p()//B.P
b.getP()//A.P
在这里b.p()
调用的是B类的p方法。b.getP()
调用的是A类的p方法。注意到,super.p===A.prototype.p
返回true。所以super
当做对象使用,在普通方法中指向父类的原型。
(1)ES6 规定,在子类中通过super调用父类方法时,方法内部的this指向当前的子类实例。
class A {
constructor() {
this.x = 1;
}
print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
m() {
super.print();
}
}
let b = new B();
b.m() // 2
在这里,通过子类B的实例调用父类A的print()
打印this.x
,得到的是实例b的x属性的值2。也就是说,实际上执行的是super.print.call(this)
。
(2)由于this指向子类实例,所以如果通过super对某个属性赋值,赋值的属性会变成子类实例的属性。
class A {
constructor() {
this.x = 1;
}
}
class B extends A {
constructor() {
super();
this.x = 2;
super.x = 3;
console.log(super.x); // undefined
console.log(this.x); // 3
}
}
let b = new B();
在这里,super.x
执行get操作时访问的是父类原型,由于父类原型中没有x属性所以super.x
返回undefined
。super.x=3
执行set操作时相当于this.x=3
,由于this指向子类实例,所以this.x
打印的是3而不是2.
2.在静态方法中,指向父类。
代码如下(示例):
class Parent {
static myMethod() {
console.log('static');
}
myMethod(){
console.log('instance')
}
}
class Child extends Parent {
static myMethod() {
super.myMethod();
console.log(super.myMethod===Parent.myMethod)//true
console.log(super.myMethod===Parent.prototype.myMethod)//false
}
}
Child.myMethod(); // static
在这里super.myMethod()
相当于Parent.myMethod()
,所以打印的值是static
。再一个是,可以看到,super.myMethod===Parent.myMethod//true
,super.myMethod===Parent.prototype.myMethod//false
这说明了super在静态方法之中指向父类而不是父类原型。
(1)在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例。
class A{
constructor(){
this.x=1
}
static print(){
console.log(this.x)
}
}
class B extends A{
constructor(){
super()
this.x=2
}
static print(){
super.print()
}
}
B.print()//undefined B.x
B.x=3
B.print()//3
这里B.print()
静态方法里的super.print
指向父类的静态方法,这个方法里的this指向子类B,而不是B的实例,即this.x===B.x
。第一次B.print()时,B类没有静态属性x,只有实例属性x,所以打印的是undefined
而不是2。设置B.x=3
后,B类静态属性x值为3,所以B.print()
打印的是3。
(2)使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。
super() //表示super作为函数使用
super.xxx //表示super作为对象使用