1、简介
类Class 可以通过extends
关键字实现继承,让子类继承父类的属性和方法。extends 的写法比 ES5 的原型链继承,要清晰和方便很多。
class Foo {
constructor(x, y) {
this.x = x;
this.y = y;
console.log('父类构造函数')
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
class Baroo extends Foo {
constructor(x, y, color) {
this.color = color; // ReferenceError super(x, y)之后才能使用子类this
super(x, y); // 调用父类的constructor(x, y) 目的是新建子类实例
this.color = color;
console.log(x,y,color)
console.log('子类构造函数')
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}
let baroo = new Baroo(1,2,'#ffffff') // 1,2,'#ffffff'
console.log(baroo) //Baroo {x: 1, y: 2, color: '#ffffff'}
console.log(baroo.toString()) // #ffffff (1, 2)
上面示例中,constructor()
方法和toString()
方法内部,都出现了super
关键字。super
在这里表示父类的构造函数,用来新建一个父类的实例对象
ES6 规定,子类必须在constructor()
方法中调用super()
,否则就会报错。这是因为子类自己的this
对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,添加子类自己的实例属性和方法。如果不调用super()
方法,子类就得不到自己的this
对象。
注意,这意味着新建子类实例时,父类的构造函数必定会先运行一次。
上面示例中,子类 Bar 新建实例时,会输出 "父类构造函数","子类构造函数"。原因就是子类构造函数调用super()
时,会执行一次父类构造函数(并在调用super()后才能使用子类的this,否则会报错)
// 如果子类没有定义constructor()方法,这个方法会默认添加,并且里面会调用super()。
// 也就是说,不管有没有显式定义,任何一个子类都有constructor()方法。
class ColorPoint extends Point {
}
// 等同于
class ColorPoint extends Point {
constructor(...args) {
super(...args);
}
}
2、super关键字
super
这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
第一种情况,super
作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super()
函数。
class Foo {}
class Boo extends Foo {
constructor() {
super();
}
}
//子类B的构造函数之中的super(),代表调用父类的构造函数。这是必须的,否则报错
注意,这里的super
虽然代表了父类的构造函数,但是因为返回的是子类的this
(即子类的实例对象),所以super
内部的this
代表子类的实例,而不是父类的实例,这里的super()
相当于A.prototype.constructor.call(this)
(在子类的this
上运行父类的构造函数)。
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
super();
}
}
new A() // A
new B() // B
上面示例中,new.target
指向当前正在执行的函数。可以看到,在super()
执行时(new B()
),它指向的是子类B
的构造函数,而不是父类A
的构造函数。也就是说,super()
内部的this
指向的是B