派生类的方法可以通过super
关键字引用它们的原型。这个关键字只能在派生类中使用,而且仅限于类构造函数、实例方法和静态方法内部。
在类构造函数中使用super
可以调用构造函数。
class Vehicle{
constructor() {
this.hasEngine = true
}
}
class Bus extends Vehicle{
constructor(){
// 不要在调用super之前引用this,否则会抛出错误
super(); //相当于super.constructor
console.log(this instanceof Vehicle); //true
console.log(this); //Bus {hasEngine: true}
}
}
new Bus()
在静态方法中可以通过super
调用继承的类上调用的方法
class Vehicle{
static identify(){
console.log("vehicle");
}
}
class Bus extends Vehicle{
static identify(){
super.identify();
}
}
Bus.identify() //vehicle
注意:
ES6给类构造函数和静态方法添加了内部特性[[HomeObject]]
,这个特性是一个指针,指向定义该方法的对象。这个指针是自动赋值的,而且只能在JavaScript
引擎内部访问。super
始终会定义为[[HomeObject]]
。
以上内容摘抄自红宝书第四版258-259页
作用:
super
这个关键字,既可以当做函数使用,也可以当做对象使用。这两种情况下,它的用法完全不用。
- 当作函数使用
class B extends A{
constructor(){
super();
}
}
在constructor
中必须调用super
方法,因为子类没有自己的this
对象,而是继承父类的this
对象,然后对其进行加工,而super
就代表了父类的构造函数。super
虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super
内部的this
指的是B,因此super()
在这里类似于A.prototype.constructor.call(this,props)
,但不相同。
class A{
constructor (){
console.log(new.target.name); //new.target指向当前正在执行的函数
}
}
class B extends A{
constructor(){
super();
}
}
new A(); //A
new B(); //B
可以看到,在super()
执行时,它指向的是子类B的构造函数,而不是父类A的构造函数。也就是说,super()
内部的this
指向的是B。
2. 当作对象使用
在普通方法中,指向父类的原型对象;在静态方法中,指向父类
class A{
c(){
return 2;
}
}
class B extends A{
constructor(){
super();
console.log(super.c()); //2
}
}
let b = new B()
子类B当中的super().c
,就是将super
当作一个对象使用。这时,super
在普通方法之中,指向A.prototype
,所以super.c()
就相当于A.prototype.c()
通过super
调用父类的方法时,super
会绑定子类的 this
。
class A {
constructor (){
this.x = 1;
}
s(){
console.log(this.x);
}
}
class B extends A{
constructor(){
super();
this.x = 2;
}
m(){
super.s()
}
}
let b = new B()
b.m(); //2
super.s()
虽然调用的是A.prototytpe.s()
,但是A.prototytpe.s()
会绑定子类B
的this
,导致输出的是 2,而不是 1。也就是说,实际上执行的是super.s.call(this)
由于绑定子类的this
,所以如果通过super
对某个属性赋值,这时super
就是 this
,赋值的属性会变成子类实例的属性。
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
赋值为3,这时等同于对this.x
赋值为3。而当读取super.x
的时候,调用的是A.prototype.x
,但是并没有x
方法,所以返回indexfined
注意,使用super
的时候,必须显式指定是作为函数。还是作为对象使用,否则会报错。
class A{
}
class B extends A{
constructor(){
super();
console.log(super); //'super' keyword unexpected here
}
}
console.log(super)
当中的super
,无法看出是作为函数使用,还是作为对象使用,所以JavaScript
引擎解析代码的时候就会报错。但是,如果能清晰的表明super
的数据类型,就不会报错。
由于对象总是继承其他对象的,所以可以在任意一个对象中,使用super
关键字.
以下内容摘抄自红宝书第四版260-261页
在使用super时要注意几个问题:
super
只能在派生类构造函数和静态方法中使用
class Vehicle{
constructor(){
super(); //'super' keyword unexpected here
}
}
- 不能单独引用
super
关键字,要么用它调用构造函数,要么用它引用静态方法
class Vehicle {}
class Bus extends Vehicle{
constructor(){
console.log(super); //'super' keyword unexpected here
}
}
- 调用
super()
会调用父类构造函数,并将返回的实例赋值给this
class Vehicle{
}
class Bus extends Vehicle{
constructor(){
super();
console.log(this instanceof Vehicle);
}
}
new Bus(); //true
super()
的行为如同调用构造函数,如果需要构造函数传参,则需要手动传入。
class Vehicle {
constructor(licensePlate){
this.licensePlate = licensePlate
}
}
class Bus extends Vehicle{
constructor(licensePlate){
super(licensePlate)
}
}
console.log(new Bus("xxxxxx")); //Bus {licensePlate: "xxxxxx"}
- 如果没有定义类构造函数,在实例化派生类时会调用
super()
,而且会传入所有传给派生类的参数
class Vehicle {
constructor(licensePlate){
this.licensePlate = licensePlate
}
}
class Bus extends Vehicle{
}
console.log(new Bus("xxxxxxx")); //Bus {licensePlate: "xxxxxxx"}
- 在类构造函数中,不能在调用
super()
之前引用this
class Vehicle{
}
class Bus extends Vehicle{
constructor(){
console.log(this);
}
}
new Bus() //Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
- 如果在派生类中显示了定义了构造函数,则要么必须在其中调用
super()
,要么必须在其中返回一个对象。
class Vehicle{
}
class Car extends Vehicle{
}
class Bus extends Vehicle{
constructor(){
super();
}
}
class Van extends Vehicle{
constructor(){
return {}
}
}
console.log(new Car()); //Car {}
console.log(new Bus()); //Bus {}
console.log(new Van()); //{}
参考文章:
红宝书第四版258-261页
简书-BluesCurry:理解 es6 class 中 constructor 方法 和 super 的作用