继承以及Super
一个小小的总结,主要关注以下三个问题:ES5的继承方式,ES5的继承与ES6的继承的区别,ES6的super的几种使用方式以及其中this的指向。
一、ES5的继承
MDN | Object.create | 用 Object.create
实现类式继承
继承可以分为对象实例的继承,类的继承
二、ES6的继承
Class B extends A { } 中的A可以是个class,还可以是个有prototype属性的函数
三、ES5继承与ES6继承的区别
this的区别
ES5 的继承,实质是先创造子类的实例对象
this
,然后再将父类的方法添加到this
上面(Parent.apply(this)
)。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this
上面(所以必须先调用super
方法),然后再用子类的构造函数修改this
。原型链ES6有两条
1
2
3
4
5class A { }
class B extends A { }
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true这样的结果是因为,类的继承是按照下面的模式实现的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14class A { }
class B { }
// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);
// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);
const b = new B();
//setPrototypeOf的内部实现
Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto;
return obj;
}
//但是setPrototypeOf会有性能问题,通常推荐使用Object.createES6可以继承原生构造函数,而ES5不能
原生构造函数有:Boolean()、Number()、String()、Array()、Date()、Function()、RegExp()、Error()、Object()
原因:
ES5 是先新建子类的实例对象
this
,再将父类的属性添加到子类上,由于父类的内部属性无法获取,导致无法继承原生的构造函数。ES6 允许继承原生构造函数定义子类,因为 ES6 是先新建父类的实例对象
this
,然后再用子类的构造函数修饰this
,使得父类的所有行为都可以继承。
继承
Object
的子类,有一个行为差异。1
2
3
4
5
6
7class NewObj extends Object{
constructor(){
super(...arguments);
}
}
var o = new NewObj({attr: true});
o.attr === true // false上面代码中,
NewObj
继承了Object
,但是无法通过super
方法向父类Object
传参。这是因为 ES6 改变了Object
构造函数的行为,一旦发现Object
方法不是通过new Object()
这种形式调用,ES6 规定Object
构造函数会忽略参数。
四、ES6的super及其this
作为函数:在子类构造函数中调用
super()
,super相当于父类constructor作为对象:
在子类构造函数或普通函数中使用
super.methodA()
,super相当于父类原型此时methodA中的this指向子类实例
在子类静态方法中使用
super.methodB()
,super指向父类而非原型,此时的methodB指的是父类的静态方法methodB此时methodB中的this指向子类,所以只能通过this访问到子类的静态方法和属性
在子类中使用super给属性赋值
super.father_prop = 1
,相当于子类的thisthis.father_prop=1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class 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
}
change(){
super.x = 4
console.log(super.x); //undefined
console.log(this.x); // 4
}
}
let b = new B();
b.change()使用
super
的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。1
2
3
4
5
6
7
8class A {}
class B extends A {
constructor() {
super();
console.log(super); // 报错
}
}
五、ES6 的class的本质
不存在任何继承时。
A
作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承Function.prototype
。1
2
3class A { }
A.__proto__ === Function.prototype // true
A.prototype.__proto__ === Object.prototype // true