目录
Class的继承
extends
Class 可以通过extends
关键字实现继承
父类的静态方法,也会被子类继承。
class A {
static hello() {
console.log('hello world');
}
}
class B extends A {
}
B.hello() // hello world
super
注意,使用super
的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。
class A {}
class B extends A {
constructor() {
super();
console.log(super); // 报错
}
}
super作为函数
super
作为函数调用时,代表父类的构造函数。且只能用在子类的构造函数之中,用在其他地方就会报错。
子类必须在constructor
方法中调用super
方法,否则新建实例时会报错。这是因为子类自己的this
对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super
方法,子类就得不到this
对象。
如果子类没有定义constructor
方法,constructor和super方法都会被默认添加
只有调用super
之后,才可以使用this
关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super
方法才能调用父类实例。 例如下面:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
this.color = color; // ReferenceError
super(x, y);
this.color = color; // 正确
}
}
上面代码中super
内部的this
指的是ColorPoint的实例,因此super()
在这里相当于Point.prototype.constructor.call(this)
。
super在运行后才会对该类中的成员变量赋值
下面例子会先运行super设置x=1,后运行自身中x=0,再运行super后的内容
class A {
x = 1;
constructor() {
this.x = 1;
console.log("父类运行了")
}
}
class B extends A {
x = 0
constructor() {
super();
console.log(this.x)
this.x=2
}
}
console.log(new B().x)
// 父类运行了
// 0
// 2
super作为对象
在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
class A {
x: number;
y = 1
}
A.prototype.x = 2;
class B extends A {
constructor() {
super();
// @ts-ignore
console.log(super.x) // 2
// @ts-ignore
console.log(super.y) // undefined
}
}
let b = new B();
注意:方法是在类的prototype上可以直接取到,成员变量是在new 的时候赋值的所有无法取到。
在子类普通方法中通过super
调用父类的方法时,方法内部的this
指向当前的子类实例。
例如下面代码:
super.print()
虽然调用的是A.prototype.print()
,但是A.prototype.print()
内部的this
指向子类B
的实例,导致输出的是2
,而不是1
。也就是说,实际上执行的是super.print.call(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
自定义数据结构
可以通过extend继承原生构造函数形成自定义数据结构,原生构造函数有Boolean()、Number()、String()、Array()、Date()、Function()、RegExp()、Error()、Object()。
以Array为例:下面代码中VersionedArray
会通过commit
方法,将自己的当前状态生成一个版本快照,存入history
属性。revert
方法用来将数组重置为最新一次保存的版本。除此之外,VersionedArray
依然是一个普通数组,所有原生的数组方法都可以在它上面调用。
class VersionedArray extends Array {
constructor() {
super();
this.history = [[]];
}
commit() {
this.history.push(this.slice());
}
revert() {
this.splice(0, this.length, ...this.history[this.history.length - 1]);
}
}
var x = new VersionedArray();
x.push(1);
x.push(2);
x // [1, 2]
x.history // [[]]
x.commit();
x.history // [[], [1, 2]]
x.push(3);
x // [1, 2, 3]
x.history // [[], [1, 2]]
x.revert();
x // [1, 2]
获取父类
Object.getPrototypeOf
方法可以用来从子类上获取父类。
class A {
}
class B extends A {
}
Object.getPrototypeOf(B) === A
// true
类的prototype和__proto__属性
子类的__proto__
属性,表示构造函数的继承,总是指向父类。
子类prototype
属性的__proto__
属性,表示方法的继承,总是指向父类的prototype
属性。
class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
实例的__proto__属性
子类实例的__proto__
属性的__proto__
属性,指向父类实例的__proto__
属性。
var p1 = new Point(2, 3);
var p2 = new ColorPoint(2, 3, 'red');
p2.__proto__ === p1.__proto__ // false
p2.__proto__.__proto__ === p1.__proto__ // true