一、原理
首先看下mdn上的解释。
Object.create()方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。
按照文档上说,我们手写实现这个方法,只需要将已有对象赋值给新对象的显示原型上即可。 目前确实有很多简单的写法就以这种方式进行。
在贴出代码之前我们需要了解下原型链
- 显示原型:
prototypejs构造函数中都有这个属性,在原型链中承担一个枢纽的作用,其本身也是一个实例对象。 - 隐示原型:
_proto_此属性存在实例对象中,指向构造函数的prototype。
从上图中,可以一窥原型链的整体面貌,除去显示隐示原型之外。
- 我们在写子类的时候,需要注意将本身的构造函数赋值给自己的
prototype上,避免在原型递进查找中使用了父类的构造函数。 - 原型链只对引用类型生效,js的原始类型不存在此机制。例如number、boolean、string。
- 当前实例对象访问属性不存在时,会顺着原型链向上查找父类、爷爷类、祖类查找。
- 祖类的原型为null,避免造成死循环。
上述应该简单了解了原型链的原理,现在再回到一开始create的定义:使用现有的对象来作为新创建对象的原型。
其实就是这一步,将父类的一个对象实例赋值给当前类的原型上,然后new出一个子类对象实例。
二、 代码实现
代码很简单,利用一个闭包就可以在函数内部创建一个这样的子类对象实例。
function create(obj) {
function F() {}
F.prototype = obj
return new F()
}
对、这样我们完成了create的任务,但是在原型链中,还有一个地方是create定义未说明的,就是Fo.prototype.construct,我们在上述原型链第一点特性中也说到需要注意将本身的构造函数赋值给自己的 prototype上。
所以我们给代码稍微做个改造:
function create(obj) {
function F() {}
F.prototype = obj
F.prototype.constructor = F
return new F()
}
最后在网上找到一佐证资料,Object.create() - JavaScript | MDN (mozilla.org)
如果未将 Rectangle.prototype.constructor 设置为 Rectangle, 它将采用 Shape (父级)的原型构造函数。 为了避免这种情况,我们将 prototype.constructor 设置为 Rectangle(子)。