ES6的 class
大部分功能都可以通过ES5来实现,所以也就有ES6的 class
只是语法糖的说法,但也有一些认为并不是所有 class 的特性都可以被es5实现,也就认为并不是是语法糖了。
这里不纠结语法糖的概念,只是用es5去实现ES6 class
的一些特性,主要参考 babel、Bublé 的实现。
寄生组合式继承
先回忆下 寄生组合式继承
,这个是红皮书里面在介绍OOP继承时最后最完美的继承方法,其它的继承方法名字也不记得了。
抄来的代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33// 继承原型的方法
function inheritPrototype(SubType, SuperType){
// 设置派生类的原型为基类原型的副本
// 并修正原型的 constructor 属性指向到派生类
SubType.prototype= Object.create(SuperType.prototype);
SubType.prototype.constructor=SubType;
}
// 基类
function Person(name){
this.name=name;
}
Person.prototype.sayName= function() {
console.log(this.name + ' ' + this.gender +' '+this.age);
}
// 派生类
function Child(name, gender, age){
Person.call(this, name); // 执行基类的构造函数,获取到基类构造函数中的 实例方法/属性
this.age=age;
this.gender=gender;
}
// 继承
inheritPrototype(Child, Person);
Child.prototype.sayAge=function() {
console.log(this.name + ' '+this.age);
}
// 实例化派生类 男
var child = new Child('Liu.Jun','male', 18);
child.sayName(); // Liu.Jun male 19
child.sayAge(); // Liu.Jun 19
原型链和继承
聊到ES5的继承感觉绕不开这里,那在顺带回忆下。
- 原型链这个只做一个简单的图。
flowchart LR subgraph 派生类 Fn(Fn - 派生类)-- prototype --> prototype(Fn.prototype - 派生类原型) prototype -- constructor --> Fn end subgraph 基类 BaseFn(BaseFn - 基类) -- prototype --> basePrototype(BaseFn.prototype - 基类原型) basePrototype -- constructor --> BaseFn end subgraph Object类 Object(Object) -- prototype --> objectPrototype(Object.prototype) objectPrototype -- constructor --> Object end prototype -- __proto__ --> basePrototype basePrototype -- __proto__ --> objectPrototype objectPrototype -- __proto__ --> null instance(instance - new Fn) -- __proto__ --> prototype
一些描述:
- 构造函数
prototype
构造函数原型属性 prototype
constructor
指向它的构造函数- 实例
__proto__
原型,指向其构造函数的prototype
原型属性 - 函数
prototype
__proto__
属性指向到Object
(箭头函数除外) - Object
prototype
__proto__
属性指向到null
取值:
- 读取一个实例的属性,如果找不到,就会查找其原型 (
__proto__
) 中的属性,找不到再继续查找原型的原型。 - 一个实例的原型也是另一个类的实例那就构成了上图中的模型,也就是
原型链
的基本概念。
再回去看 寄生组合式继承
的代码实现就是这里的图
New发生了啥?
- 创建一个空对象 {}
- 对象
__proto__
属性指向构造函数的原型 - 执行构造函数,传入对象作为执行上下文
- 如果执行结果是简单类型就直接对象,否则返回执行结果
参见如下方法:newObjectFactory
1 | function Person(name) { |
ES6 class
还是如上的例子,ES6 class 继承
1 | class Person { |
Bublé
转换后:
1 | var Person = function Person(name) { |
这里转换也是使用的
寄生组合式继承
,区别就是会让派生类的__proto__
指向到基类保持和class
一致,Child.__proto__ = Person
,类似于babel
的loose
模式转换。
这里是因为Class的继承同时存在两条继承链
- 派生类的
__proto__
属性,表示构造函数的继承,总是指向基类。- 派生类prototype属性的
__proto__
属性,表示方法的继承,总是指向基类的prototype
属性。也就是如下代码
1
2 Child.__proto__ = Person;
Child.prototype = Object.create(Person && Person.prototype);
区别
ES5实现和ES6 Class 有一个本质的区别
- ES5 的继承,实质是先创造子类的实例对象
this
,然后再将父类的方法添加到this
上面(Parent.call(this)
)。- ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到
this
上面(所以必须先调用super
方法),然后再用子类的构造函数修改this
。- 这里也就是常说的
ES6子类
无this