Class的继承
简介
Class可以通过extends关键字实现继承。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super();
this.color = color;
}
}
子类必须在构造方法中调用super方法,否则新建实例时会报错。ES6的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
如果子类没有定义构造方法,会默认添加构造方法。
class Point {}
//等同于
class ColorPoint extends Point {
constructor(...args) {
super(...args);
}
}
在子类的构造函数中,只有调用super之后,才能使用this。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。
从下面的代码可以看出,子类的实例对象同时是父类和子类两个类的实例。
var cp = new ColorPoint(15, 25, "orange");
cp instanceof ColorPoint; //true
cp instanceof Point; //true
最后,子类会继承父类的静态方法。
Object.getPrototypeOf()
Objetc.getPrototypeOf可以用来从子类上获取父类。因此,这个方法可以用来判断一个类是否继承了另一个类。
console.log(Object.getPrototypeOf(ColorPoint) == Point); //true
super关键字
super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
第一种情况,super作为函数调用时,只能用在构造函数中,代表父类的构造函数。
class A {}
class B extends A {
constructor() {
super();
}
}
super虽然代表父类A的构造函数,但返回的是子类B的实例。即super内部的this指的是B的实例,因此super()在这里相当于A.prototype.constructor.call(this);
在下面的代码中new.target指向的是正在执行的函数。new B()输出的是B,说明super内部的this指向的是B。
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
super();
}
}
new A(); //A
new B(); //B
作为函数,super只能用在构造函数中,否则就会报错。
class A {}
class B extends A {
m() {
super();
}
}
第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
下面的代码中,子类B当中的super.p(),就是将super当作一个对象使用。这时,super在普通方法之中,指向A.prototype,所以super.p()就相当于A.prototype.p()。因为prop是父类的实例属性,所以就访问不到。
class A {
constructor() {
this.prop = 1;
}
m() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.m()); // 2
console.log(super.prop); //undefined
}
}
var b = new B();
在子类普通方法中通过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();
}
}
var b = new B();
b.m(); // 2
上面代码中,super.print()虽然调用的是A.prototype.print(),但是A.prototype.print()内部的this指向子类B的实例,导致输出的是2,而不是1。也就是说,实际上执行的是super.print.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
}
}
var b = new B();
上面的代码中,super.x赋值为3,实际上就是this.x赋值为3。读取super.x的时候,读取的是A.prototype.x。
super作为对象时,在静态方法中指向父类,在普通方法中指向父类原型对象。
class A {
static myMethod(msg) {
console.log("static", msg);
}
myMethod(msg) {
console.log("instance", msg);
}
}
class B extends A {
constructor() {
super();
}
static myMethod(msg) {
super.myMethod(msg);
}
myMthod(msg) {
super.myMethod(msg);
}
}
B.myMethod(1); //static 1
let b = new B();
b.myMethod(2); //instance 2
在子类静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例。
class A {
constructor() {
this.x = 1;
}
static print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
static m() {
super.print();
}
}
B.x = 3;
B.m(); //3