In JavaScript, le funzioni possono avere proprietà. Tutte le funzioni hanno una proprietà speciale chiamata prototype
.
function doSomething() {}
console.log(doSomething.prototype);
// Le funzioni in JavaScript hanno sempre una proprietà prototype di default
// Eccezione: le funzioni freccia non hanno una proprietà prototype di default
const doSomethingFromArrowFunction = () => {};
console.log(doSomethingFromArrowFunction.prototype);
doSomething()
ha una proprietà prototype
di default. Dopo aver eseguito il codice sopra, la console mostrerà un oggetto simile al seguente:
{ constructor: ƒ doSomething(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } }
Nota: la console di Chrome usa [[Prototype]]
per rappresentare il prototipo dell’oggetto, secondo la terminologia della specifica; Firefox usa .prototype
. Per uniformità, useremo [[Prototype]]
.
Possiamo aggiungere proprietà al prototipo di doSomething()
:
function doSomething() {}
doSomething.prototype.foo = "bar";
console.log(doSomething.prototype);
Risultato:
{ foo: "bar", constructor: ƒ doSomething(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } }
Usiamo l’operatore new
per creare un’istanza di doSomething()
basata su questo prototipo. Chiamare una funzione con l’operatore new
restituisce un oggetto che è un’istanza della funzione. Le proprietà possono quindi essere aggiunte a questo oggetto.
function doSomething() {}
doSomething.prototype.foo = "bar"; // aggiungi una proprietà al prototipo
const doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // aggiungi una proprietà all'oggetto
console.log(doSomeInstancing);
Risultato:
{ prop: "some value", [[Prototype]]: { foo: "bar", constructor: ƒ doSomething(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } } }
[[Prototype]]
di doSomeInstancing
è doSomething.prototype
. Quando si accede a una proprietà di doSomeInstancing
, il runtime prima controlla se doSomeInstancing
ha quella proprietà.
In caso contrario, il runtime cercherà la proprietà in doSomeInstancing.[[Prototype]]
(cioè doSomething.prototype
). Se doSomeInstancing.[[Prototype]]
ha la proprietà che si sta cercando, allora quella proprietà verrà utilizzata.
Altrimenti, verrà controllato doSomeInstancing.[[Prototype]].[[Prototype]]
. Per impostazione predefinita, [[Prototype]]
della proprietà prototype
di qualsiasi funzione è Object.prototype
. Quindi, verrà cercato doSomeInstancing.[[Prototype]].[[Prototype]]
(cioè doSomething.prototype.[[Prototype]]
o Object.prototype
). Questo processo è chiamato catena di prototipi.
Se la proprietà non viene trovata in doSomeInstancing.[[Prototype]].[[Prototype]]
, verrà cercato doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]
. Tuttavia, doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]
non esiste, perché Object.prototype.[[Prototype]]
è null
. Dopo che l’intera catena di prototipi è stata cercata, il runtime affermerà che la proprietà non esiste e il valore della proprietà è undefined
.
function doSomething() {}
doSomething.prototype.foo = "bar";
const doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value";
console.log("doSomeInstancing.prop: ", doSomeInstancing.prop);
console.log("doSomeInstancing.foo: ", doSomeInstancing.foo);
console.log("doSomething.prop: ", doSomething.prop);
console.log("doSomething.foo: ", doSomething.foo);
console.log("doSomething.prototype.prop:", doSomething.prototype.prop);
console.log("doSomething.prototype.foo: ", doSomething.prototype.foo);
Risultato:
doSomeInstancing.prop: some value
doSomeInstancing.foo: bar
doSomething.prop: undefined
doSomething.foo: undefined
doSomething.prototype.prop: undefined
doSomething.prototype.foo: bar