Attention : Étant donnée la façon dont la plupart des moteurs JavaScript optimisent les performances, modifier le [[Prototype]]
d'un objet est une opération lente pour chaque navigateur et moteur JavaScript. Les impacts liés aux performances sur ce point sont vastes et subtiles : ils concernent pas uniquement le temps passé à effectuer obj.__proto__ = ...
, mais peuvent concerner n'importe quel code pour n'importe quel objet dont [[Prototype]]
a été modifié. Si vous souhaitez obtenir des performances optimales, évitez de modifier le [[Prototype]]
d'un objet. À la place, il est conseillé de créer un objet avec le prototype voulu en utilisant Object.create()
.
Attention : Bien que la propriété Object.prototype.__proto__
soit déjà supportée dans la plupart des navigateurs à l'heure actuelle, son comportement n'a été standardisé que récemment avec la spécification ECMAScript 6. Si vous avez besoin d'utiliser cette propriété dans des environnements antérieurs à ES6, il est recommandé d'utiliser Object.getPrototypeOf()
.
La propriété __proto__
de Object.prototype
est une propriété accesseur (un couple de fonction avec un accesseur (getter) et un mutateur (setter)) qui expose le [[Prototype]]
interne (qui est soit un objet, soit null
) de l'objet courant.
L'utilisation de __proto__
est sujet à controverse. Elle a été déconseillée par plusieurs personnes et n'avait jamais été incluse dans la spécification ECMAScript. Cependant, de nombreux navigateurs ont décidé de l'implémenter. À l'heure actuelle, la propriété __proto__
a été standardisée avec la spécification ECMAScript 6 et sera officiellement supportée à l'avenir. Une alternative à cette propriété peut être l'utilisation des méthodes Object.getPrototypeOf
/Reflect.getPrototypeOf
et Object.setPrototypeOf
/Reflect.setPrototypeOf
. Cependant, modifier le [[Prototype]]
d'un objet est toujours une opération lente qui doit être évitée le plus possible pour des raisons de performances.
La propriété __proto__
peut également être utilisée avec un littéral objet afin de définir le [[Prototype]]
lors de la construction (ce qui en fait une alternative à Object.create()
. Voir la page sur les initialisateurs d'objet.
Syntaxe
var proto = obj.__proto__;
Note : le nom de la propriété est composé de deux tirets bas, suivis de « proto », suivis par deux tirets bas (underscores)
Description
L'accesseur __proto__
expose la valeur du [[Prototype]]
interne d'un objet.
- Pour les objets créés via un littéral objet, cette valeur est
Object.prototype
. - Pour les objet créés via un littéral de tableau, cette valeur est
Array.prototype
. - Pour les fonctions, cette valeur est
Function.prototype
. - Pour les objets créés en utilisant
new fun
, avecfun
un des constructeurs natif de fonctions, fournis par JavaScript (Array
,Boolean
,Date
,Number
,Object
,String
, etc.), cette valeur estfun.prototype
. - Pour les objets créés en utilisant
new fun
, avecfun
une function definie dans un script, cette valeur est la valeur defun.prototype
au moment oùnew fun
est évaluée. (Ainsi, si on affecte une nouvelle valeur àfun.prototype
, les instances crées précédemment conserveront leur[[Prototype]]
, les objets créés par la suite bénéficieront de la nouvelle valeur pour leur[[Prototype]]
.)
Le mutateur __proto__
permet de changer le [[Prototype]]
d'un objet. Cet objet doit être extensible selon Object.isExtensible
, si ce n'est pas le cas, une exception TypeError
sera renvoyée. La valeur fournie pour le nouveau prototype doit être un objet ou null
. Toute autre valeur entraînera un échec silencieux.
Pour plus d'éléments sur le fonctionnement de l'héritage et des prototypes, voir la page sur l'héritage et les chaînes de prototypes.
Le propriété __proto__
n'est qu'une propriété accesseur (composée d'une fonction accesseur (getter) et d'une fonction mutateur (setter)) pour Object.prototype
. Si l'accès à __proto__
consulte Object.prototype
, on trouvera la propriété. Un accesseur qui ne consulte pas Object.prototype
ne pourra pas trouver le prototype. Si une propriété __proto__
est trouvée avant que Object.prototype
ne soit consulté, cette propriété « cachera » Object.prototype
.
var aucunProto = Object.create(null); console.log(typeof aucunProto.__proto__); // undefined console.log(Object.getPrototypeOf(aucunProto)); // null aucunProto.__proto__ = 17; console.log(aucunProto.__proto__); // 17 console.log(Object.getPrototypeOf(aucunProto)); // null var protoCaché = {}; Object.defineProperty(protoCaché, "__proto__", { value: 42, writable: true, configurable: true, enumerable: true }); console.log(protoCaché.__proto__); // 42 console.log(Object.getPrototypeOf(protoCaché) === Object.prototype); // true
Exemples
Dnas ce qui suit, on crée un nouvelle instance d'Employé
et on teste si __proto__
est bien le même objet que le prototype de son constructeur.
Attention ! Les remarques données plus haut sur les atteintes à la performance restent valables pour ces exemples. Ces exemples permettent uniquement d'illustrer le fonctionnement de __proto__
, ils ne font pas office de recommandations.
// On déclare une fonction à utiliser comme constructeur function Employé() { /* on initialise l'instance */ } // On crée une nouvelle instance d'Employé var fred = new Employé(); // On teste l'équivalence fred.__proto__ === Employé.prototype; // true
À cet instant, fred
hérite de Employé
. On peut toutefois changer ça en assignant un nouvel objet à fred.__proto__
:
// Assigner un nouvel objet à __proto__ fred.__proto__ = Object.prototype;
fred
n'hérite plus de Employé.prototype
, mais de Object.prototype
. Il perd donc les propriétés héritées de Employé.prototype
.
Cela n'est possible que pour les objets extensibles. La propriété __proto__
d'un objet non-extensible ne peut pas être changée :
var obj = {}; Object.preventExtensions(obj); obj.__proto__ = {}; // renvoie une exception TypeError
On notera que même la propriété __proto__
de Object.prototype
peut être redéfinie tant que la chaîne de prototypes se termine par null
:
var b = {}; Object.prototype.__proto__ = Object.create(null, //[[Prototype]] { salut: { value: function () {console.log('salut');}}}); b.salut();
Si la propriété __proto__
de
Object.prototype
ne permet pas d'aboutir à null
via la chaîne de prototypes, on a une chaîne cyclique et on doit avoir une exception TypeError
"cyclic __proto__ value".
Spécifications
Spécification | Statut | Commentaires |
---|---|---|
ECMAScript 2015 (6th Edition, ECMA-262) La définition de 'Object.prototype.__proto__' dans cette spécification. |
Standard | Incluse dans l'annexe (normative) pour le fonctionnalités additionneles d'ECMAScript pour les navigateurs web (note : la spécification codifie ce qui est déjà présent dans les implémentations). |
ECMAScript 2017 Draft (ECMA-262) La définition de 'Object.prototype.__proto__' dans cette spécification. |
Projet |
Compatibilité des navigateurs
Fonctionnalité | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
Support simple | (Oui) | (Oui) | 11 | (Oui) | (Oui) |
Fonctionnalité | Android | Chrome pour Android | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|
Support simple | (Oui) | (Oui) | (Oui) | (Oui) | (Oui) | (Oui) |
Notes de compatibilité
Bien que la spécification ES6 rende le support de __proto__
nécessaire pour les navigateurs web, elle n'est pas obligatoire pour les autres environnements (bien que ce soit conseillé vu le caractère normatif de l'annexe). Si votre code doit être compatible avec un environnement qui n'est pas un navigateur web, il est recommandé d'utiliser Object.getPrototypeOf()
et Object.setPrototypeOf()
à la place.