En JavaScript, les fonctions possèdent des propriétés. Chaque fonction a une propriété spéciale nommée prototype
.
function faireQuelqueChose() {}
console.log(faireQuelqueChose.prototype);
// En JavaScript, une fonction a toujours une propriété prototype par défaut.
// Exception : les fonctions fléchées n'ont pas de propriété prototype par défaut.
const faireQuelqueChoseFleche = () => {};
console.log(faireQuelqueChoseFleche.prototype);
faireQuelqueChose()
possède une propriété prototype
par défaut. Après l’exécution du code ci-dessus, la console affichera un objet semblable à celui-ci :
{ constructor: ƒ faireQuelqueChose(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } }
Remarque : La console de Chrome utilise [[Prototype]]
pour représenter le prototype d’un objet, conformément à la spécification; Firefox utilise .prototype
. Par souci d’uniformité, nous utiliserons [[Prototype]]
.
Nous pouvons ajouter des propriétés au prototype de faireQuelqueChose()
:
function faireQuelqueChose() {}
faireQuelqueChose.prototype.foo = "bar";
console.log(faireQuelqueChose.prototype);
Résultat :
{ foo: "bar", constructor: ƒ faireQuelqueChose(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } }
L’opérateur new
permet de créer une instance de faireQuelqueChose()
basée sur ce prototype. Appeler une fonction avec l’opérateur new
renvoie un objet qui est une instance de la fonction. Des propriétés peuvent ensuite être ajoutées à cet objet.
function faireQuelqueChose() {}
faireQuelqueChose.prototype.foo = "bar"; // ajout d'une propriété au prototype
const instance = new faireQuelqueChose();
instance.prop = "une valeur"; // ajout d'une propriété à l'objet
console.log(instance);
Résultat :
{ prop: "une valeur", [[Prototype]]: { foo: "bar", constructor: ƒ faireQuelqueChose(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } } }
Le [[Prototype]]
de instance
est faireQuelqueChose.prototype
. Lors de l’accès à une propriété de instance
, le runtime vérifie d’abord si instance
possède cette propriété.
Sinon, le runtime recherche la propriété dans instance.[[Prototype]]
(c’est-à-dire faireQuelqueChose.prototype
). Si instance.[[Prototype]]
possède la propriété recherchée, alors cette propriété est utilisée.
Sinon, instance.[[Prototype]].[[Prototype]]
est vérifié. Par défaut, le [[Prototype]]
de la propriété prototype
de toute fonction est Object.prototype
. Donc, instance.[[Prototype]].[[Prototype]]
(c’est-à-dire faireQuelqueChose.prototype.[[Prototype]]
ou Object.prototype
) est recherché. Ce processus est appelé chaîne de prototypes.
Si la propriété n’est pas trouvée dans instance.[[Prototype]].[[Prototype]]
, alors instance.[[Prototype]].[[Prototype]].[[Prototype]]
est recherché. Cependant, instance.[[Prototype]].[[Prototype]].[[Prototype]]
n’existe pas, car Object.prototype.[[Prototype]]
est null
. Une fois toute la chaîne de prototypes parcourue, le runtime détermine que la propriété n’existe pas et sa valeur est undefined
.
function faireQuelqueChose() {}
faireQuelqueChose.prototype.foo = "bar";
const instance = new faireQuelqueChose();
instance.prop = "une valeur";
console.log("instance.prop: ", instance.prop);
console.log("instance.foo: ", instance.foo);
console.log("faireQuelqueChose.prop: ", faireQuelqueChose.prop);
console.log("faireQuelqueChose.foo: ", faireQuelqueChose.foo);
console.log("faireQuelqueChose.prototype.prop:", faireQuelqueChose.prototype.prop);
console.log("faireQuelqueChose.prototype.foo: ", faireQuelqueChose.prototype.foo);
Résultat :
instance.prop: une valeur
instance.foo: bar
faireQuelqueChose.prop: undefined
faireQuelqueChose.foo: undefined
faireQuelqueChose.prototype.prop: undefined
faireQuelqueChose.prototype.foo: bar