Ответ 1
Крокфорд рекомендует этот вид Object.create
shim:
if (typeof Object.create != "function") {
Object.create = function (o) {
function F(){}
F.prototype = o;
return new F;
};
}
Но, пожалуйста, не делайте этого.
Проблема с этим подходом заключается в том, что ES5 Object.create
имеет подпись 2 аргумента: first - объект для наследования и второй (необязательный) - объект, представляющий свойства (или, скорее, дескрипторы) для добавления к вновь созданному объекту.
Object.create(O[, Properties]); // see 15.2.3.5, ECMA-262 5th ed.
У нас есть непоследовательная реализация с двумя разными типами поведения. В средах с native Object.create
метод знает, как обрабатывать второй аргумент; в средах без native Object.create
, это не так.
Каковы практические последствия?
Ну, если есть какой-то код (скажем, сторонний script), который хочет использовать 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;
};
}
Обратите внимание, что "свойства" - это объект, представляющий дескрипторы свойств, а не только имена/значения свойств и что-то, что не очень тривиально для поддержки (некоторые вещи даже не возможны, например, контроль перечислимости свойства):
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);
Это создает объект, чей [[Прототип]] null
; другими словами, объект, который не наследует ни от чего, даже Object.prototype
(который наследует все нативные объекты в ECMAScript).
foo.toString; // undefined
foo.constructor; // undefined
// etc.
Это, кстати, полезно для создания "правильных" хэш-таблиц в ECMAScript.
Возможно эмулировать это поведение, но только с использованием нестандартных расширений, таких как "магическое" свойство __proto__
(поэтому реализация будет не очень переносимой или надежной). Решение этой проблемы аналогично: либо полностью эмулировать реализацию ES5, либо уведомлять о несогласованности/сбое.