Крокфорд рекомендует этот вид 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
существует, он должен соответствовать спецификациям - принять второй аргумент и добавить соответствующие свойства к объекту.
Но с вышеупомянутой прокладкой второй аргумент просто игнорируется. Нет даже признаков того, что что-то пошло неправильно по-другому. Так сказать, тихая ошибка, которую довольно больно обнаружить и исправить.
Можем ли мы сделать лучше?
Ну, на самом деле невозможно создать полностью соответствующую Object.create
шимму, используя только (стандартные) средства ES3 . Лучшее решение - создать собственный метод-обертку.
Однако есть несколько альтернативных (менее чем оптимальных) вещей, которые вы можете попробовать:
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;
};
}
Обратите внимание, что "properties" - это объект, представляющий дескрипторы свойств , а не только имена / значения свойств, и его не так-то просто поддерживать (некоторые вещи даже невозможны, например, управление перечислимостью собственность):
Object.create(parent, {
foo: {
value: 'bar',
writable: true
},
baz: {
get: function(){ return 'baz getter'; },
set: function(value){ return 'baz setter'; },
enumerable: true
}
});
Другое несоответствие в оригинальной прокладке заключается в том, что она не заботится о том, чтобы родительский объект был null
.
var foo = Object.create(null);
Это создает объект, чей [[Prototype]] равен null
; другими словами, объект, который не наследуется ни от чего, даже от Object.prototype
(от которого наследуются все нативные объекты в ECMAScript).
foo.toString; // undefined
foo.constructor; // undefined
// etc.
Кстати, это полезно для создания «правильных» хеш-таблиц в ECMAScript.
Можно эмулировать это поведение, но только с использованием нестандартных расширений, таких как «магическое» свойство __proto__
(поэтому реализация будет не очень переносимой или устойчивой). Решение этой проблемы аналогично: либо полностью эмулируйте реализацию ES5, либо сообщайте о несогласованности / сбое.