Object.create 方法及类的继承
一、开始
本文讲解 Object.create 方法的概念、实现原理,以及如何用它实现继承。
二、概念
Object.create()
方法可以创建一个新对象,使用现有的对象来提供新创建的对象的 __proto__
。
Object.create
和 New Object
的区别如下:
1. 创建对象的方式不同
new Object()
通过构造函数来创建对象, 添加的属性是在自身实例下。
Object.create()
是 ES6 创建对象的另一种方式,可以理解为继承一个对象, 添加的属性是在原型下。
2. 创建对象属性的性质不同
Object.create()
用第二个参数来创建非空对象的属性描述符默认是为 false
的,而构造函数或字面量方法创建的对象属性的描述符默认为 true
。
这个不同是比较的 Object.create
的第二个参数与 new Object
的第一个参数。
3. 创建空对象时不同
当用构造函数或对象字面量方法创建空对象时,对象时有原型属性的,即有 __proto__
;
当用 Object.create()
方法创建空对象时,对象是没有原型属性的。
三、动手实现
Object.create
的实现原理是:
- 创建一个空的构造函数
- 将传入的对象作为其原型
- 用该构造函数创建新的对象,并返回
代码大致如下,此处没有考虑参数的兼容性问题:
四、类的继承
1. 寄生组合式继承
参考MDN,我们用ES5语法实现类的继承。代码如下:
这种方法称为寄生组合式继承。
- 寄生式继承的思路:在创建对象的函数中直接吸收其他对象的功能,然后对其进行扩展并返回。
- 构造函数的目的是为了复制属性,Parent.call(this, name)
肯定不能少
- Child.prototype = new Parent()
的目的是为了获取到父类原型对象(prototype
)上的方法,基于这个目的,有没有别的方法可以做到 在不需要实例化父类构造函数的情况下,也能得到父类原型对象上的方法呢? 当然可以,我们可以采用寄生式继承来得到父类原型对象上的方法。
- 那么使用 Object.create
的原型链是什么样的呢?其实很简单:
SubType.prototype.__ proto __ = SuperType.protype
也就是说,子类的原型相当于是父类原型的一个实例,这不就是实现了两者的链接了吗?
下面看下其他方式的不足。
2. 单纯的构造函数继承 Parent.call(this)
只用 Parent.call(this)
的缺点是,原型链上的属性并没被继承。
3. 单纯的原型链继承 Child.prototype = new Parent()
使用 Child.prototype = new Parent()
的缺点是,子类实例的引用类型是公用的,修改一个实例会引起另一个实例的改变。
4. 构造函数继承和原型继承的简单叠加
简单的组合方式是,上述两种的相加。缺点是父级构造函数执行了两次。
5. Child.prototype = Parent.prototype
Child.prototype = Parent.prototype
的缺点是:
- 无法区分一个实例是哪个原型对象创建的。若强行使用过 Child.prototype.constructor = Child
,会使得父类对象的 contructor
属性也是 Child
,显然错误。
- 并且,当我们想要在子对象原型中扩展一些属性以便之后继续继承的话,父对象的原型也会被改写,因为这里的原型对象实例始终只有一个,这也是这种继承方式的缺点。