继承以及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=11
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