Crockford recommends这种Object.create垫片:
if (typeof Object.create != "function") {
Object.create = function (o) {
function F(){}
F.prototype = o;
return new F;
};
}
但是请不要这样做。
这种方法的问题在于 ES5 Object.create 具有 2 个参数 的签名:第一个 - 要继承的对象,第二个(可选)- 表示属性的对象(或者更确切地说,描述符)添加到新创建的对象。
Object.create(O[, Properties]); // see 15.2.3.5, ECMA-262 5th ed.
我们所拥有的是一个具有 2 种不同行为的不一致实现。在原生Object.create 的环境中,方法知道如何处理第二个参数;在没有原生 Object.create 的环境中,它不会。
有什么实际意义?
好吧,如果有一些代码(比如第三方脚本)想要使用Object.create,那么该代码这样做是相当合理的:
if (Object.create) {
var child = Object.create(parent, properties);
}
——基本上假设如果Object.create 存在,它必须符合规范——接受第二个参数并向对象添加相应的属性。
但是,使用上述 shim,第二个参数就被忽略了。甚至没有迹象表明发生了不同的错误。可以说是无声的失败——发现和修复相当痛苦的事情。
我们可以做得更好吗?
嗯,实际上不可能仅使用(标准)ES3 工具来创建完全符合的Object.create shim。最好的解决方案是创建自定义包装方法。
但是,您可以尝试的替代方法(不是最佳)很少:
1) 通知用户无法使用第二个参数
if (!Object.create) {
Object.create = function (o) {
if (arguments.length > 1) {
throw Error('second argument is not supported');
}
// ... proceed ...
};
}
2) 尝试处理第二个参数:
if (!Object.create) {
Object.create = function (parent, properties) {
function F(){}
F.prototype = parent;
var obj = new F;
if (properties) {
// ... augment obj ...
}
return obj;
};
}
请注意,“属性”是一个表示属性描述符的对象,而不仅仅是属性名称/值,而且支持起来并不是很简单(有些事情甚至是不可能的,例如控制可枚举性属性):
Object.create(parent, {
foo: {
value: 'bar',
writable: true
},
baz: {
get: function(){ return 'baz getter'; },
set: function(value){ return 'baz setter'; },
enumerable: true
}
});
原始 shim 的另一个不一致之处是它没有处理 父对象为 null。
var foo = Object.create(null);
这将创建一个 [[Prototype]] 为 null 的对象;换句话说,不从任何东西继承的对象,甚至不是Object.prototype(ECMAScript 中的所有本机对象都继承自)。
foo.toString; // undefined
foo.constructor; // undefined
// etc.
顺便说一下,这对于在 ECMAScript 中创建“正确的”哈希表很有用。
可以模拟这种行为,但只能使用非标准扩展,例如“神奇的”__proto__ 属性(因此实现不会非常便携或健壮)。此问题的解决方案类似:要么完全模拟 ES5 实现,要么通知不一致/失败。