Crockford recommends ce genre de Object.create
shim:
if (typeof Object.create != "function") {
Object.create = function (o) {
function F(){}
F.prototype = o;
return new F;
};
}
Mais s'il vous plaît ne le faites pas.
Le problème avec cette approche est que ES5 Object.create
a une signature de 2 arguments: d'abord - un objet à hériter de, et le second (en option) - un des propriétés représentant l'objet (ou plutôt, descripteurs) pour ajouter à nouveau objet créé.
Object.create(O[, Properties]); // see 15.2.3.5, ECMA-262 5th ed.
Ce que nous avons est une mise en œuvre incompatible avec 2 comportements différents. Dans les environnements natifs Object.create
, la méthode sait gérer le second argument; dans les environnements sans Object.create
natif, ce n'est pas le cas.
Quelles sont les implications pratiques?
Eh bien, s'il y a un code (par exemple, un script tiers) qui veut utiliser Object.create
, il est assez raisonnable pour ce code pour ce faire:
if (Object.create) {
var child = Object.create(parent, properties);
}
- essentiellement en supposant que si Object.create
existe, il doit se conformer aux spécifications - accepter le deuxième argument et ajouter les propriétés correspondantes à un objet. Mais, avec le shim mentionné ci-dessus, le second argument est simplement ignoré. Il n'y a même pas une indication de quelque chose qui va mauvais différemment. Un échec silencieux, pour ainsi dire - quelque chose qui est plutôt douloureux à détecter et à réparer.
Pouvons-nous faire mieux? Eh bien, il est en fait impossible de créer une cale entièrement conforme Object.create
en utilisant uniquement les installations ES3 (standard). La meilleure solution consiste à créer une méthode wrapper personnalisée.
Il y a, cependant, peu d'autres (moins optimales) que vous pouvez essayer:
1) Informe utilisateur sur l'incapacité de travailler avec le deuxième argument
if (!Object.create) {
Object.create = function (o) {
if (arguments.length > 1) {
throw Error('second argument is not supported');
}
// ... proceed ...
};
}
2) Essayez de gérer deuxième argument :
if (!Object.create) {
Object.create = function (parent, properties) {
function F(){}
F.prototype = parent;
var obj = new F;
if (properties) {
// ... augment obj ...
}
return obj;
};
}
Notez que les « propriétés » est un objet représentant descripteurs de propriété, non seulement les noms de propriété/val ues, et est quelque chose qui est pas très trivial de (certaines choses ne sont même pas possible, comme le contrôle de énumérabilité d'une propriété):
Object.create(parent, {
foo: {
value: 'bar',
writable: true
},
baz: {
get: function(){ return 'baz getter'; },
set: function(value){ return 'baz setter'; },
enumerable: true
}
});
L'autre incohérence dans la cale d'origine est qu'il ne prend pas soin de l'objet parent étant null
.
var foo = Object.create(null);
Cela crée un objet dont [[Prototype]] est null
; en d'autres termes, un objet qui n'hérite d'rien, pas même Object.prototype
(dont héritent tous les objets natifs d'ECMAScript).
foo.toString; // undefined
foo.constructor; // undefined
// etc.
Ceci est, par ailleurs, utile pour créer des tables de hachage "appropriées" dans ECMAScript.
Il est possible d'émuler ce comportement, mais uniquement en utilisant des extensions non standard, telles que la propriété "magique" __proto__
(donc l'implémentation ne serait pas très portable ou robuste). La solution à ce problème est similaire: soit émuler complètement l'implémentation ES5, soit notifier l'incohérence/l'échec.
Voir aussi http://stackoverflow.com/questions/1890464/is-there-a-library-which-implements-new-javascript-ecmascript-5-methods-for-older/2916063#2916063 –