首先在看这类手写代码的题目的之前,最好是上MDN或者W3C去看看这些代码在原生的JS中是用来的干什么的,它们有哪些参数。这样做有助于我们理解并模拟实现。
Object.create()用于创建一个新的以已有对象为原型的对象。该方法可以接收两个参数。
- 参数一是作为新对象原型的对象
- 参数二也是一个对象,该对象会为创建的新对象添加它的所有自有属性(被定义在对象自身而不是对象原型身上的属性),这些属性对应于Object.defineProperties()的第二个参数。(模拟实现的时候也会采用该API)
作为原型的对象如果是基本类型包装对象之外的对象(一般指null和undefined,null是特殊的所以只考虑undefined报错的情况),该方法会抛出错误。
有了这些概念,我们首先就能实现一个不那么完善的Object.create()
Object.create2 = function (proto, propertyObject = undefined) {//接收两个参数
//proto会报错的情况
if (typeof proto != 'object' && typeof proto != 'function') {
throw new TypeError('Object prototype may only be an Object or null')
}
//利用原型链让新对象原型指向参数对象
function F() {}
F.prototype = proto
const obj = new F()//对象的隐式原型指向它构造函数的显式原型
//如有第二个参数
if (propertyObject) {
Object.defineProperties(obj, propertyObject)
}
return obj
}
之所以说不完善是因为还有两个问题我们没有考虑
- propertyObject为null的时候是需要报错的,因为null身上没有任何属性
- proto为null的时候会经过第一次筛选。但是以null为原型的对象身上没有任何从Object.prototype中继承的属性和方法,比较特殊。也可以理解为proto为null的时候创建的对象就没有原型对象,这种情况需要单独列出来。
完善之后我们就可以得到这个版本
Object.create2 = function (proto, propertyObject = undefined) {//接收两个参数
//proto会报错的情况
if (typeof proto != 'object' && typeof proto != 'function') {
throw new TypeError('Object prototype may only be an Object or null')
}
//propertyObject为null应该报错
if (propertyObject == null) {
throw new TypeError('Cannot convert undefined or null to object')
}
//利用原型链让新对象原型指向参数对象
function F() {}
F.prototype = proto
const obj = new F()//对象的隐式原型指向它构造函数的显式原型
//如有第二个参数
if (propertyObject) {
Object.defineProperties(obj, propertyObject)
}
//支持创建没有原型的对象
if (proto == null) {
obj.__proto__ = null
}
return obj
}
这里如果不单独将proto为null的情况单独列出来,在执行到代码
const obj = new F()
的时候,因为F的原型被重写为null,ES规范中规定如果一个被创建对象的原型为null的时候,会将这个被创建的对象的原型自动设置为Object.ptototype。而我们给Object.create()传入null参数的本意是希望创建一个没有原型的对象,所以为null的情况需要手动修改obj的原型对象。
详细信息可以查看ES规范13.2.2的第七条中,关于原型对象是null的解决方案。