Ereditarietà e catena dei prototype

Questa traduzione è incompleta. Aiutaci a tradurre questo articolo dall’inglese

JavaScript confonde un po' gli sviluppatori che hanno esperienza di linguaggi basati sulle classi (come Java o C++), siccome è un linguaggio dinamico e non fornisce un'implementazione di class (la keyword class è introdotto in ES2015, ma è zucchero sintattico, Javascript rimarrà basato sui prototipi).

In termini di ereditarietà, Javascript ha solo un costrutto: gli oggetti. Ogni oggetto ha un link interno ad un altro oggetto chiamato prototype. Questo oggetto prototype ha a sua volta un suo prototype, e così via finché si raggiunge un oggetto con property nullnull, per definizione, non ha un prototype, ed agisce come link finale nella catena di prototipi.

Quasi tutti gli oggetti in Javascript sono istanze di Object, che risiede in cima alla catena dei prototipi.

Nonostante questo sia considerato spesso come una debolezza di Javascript, il modello di ereditarietà prototipale è invece più potente del modello classico. Per esempio, è banale costruire un classico modello sul modello prototipale, mentre il contrario è molto più difficile.

Ereditarietà con la catena di prototipi

Ereditare properties

Gli oggetti javaScript sono "contenitori" dinamici di proprietà (own properties). Gli oggetti JavaScript hanno un link ad un oggetto prototype. Provando ad accedere ad una proprietà di un oggetto, la proprietà sarà ricercata sia sull'oggetto, sia sul prototipo, sul prototipo del prototipo e così via fino a trovare una proprietà con il nome specificato fino alla fine della catena stessa.

Seguendo lo standard ECMAScript, la notazione someObject.[[Prototype]] viene usata per designare il prototype di someObject. Dall'introduzione di ECMAScript 2015, per accedere a [[Protoype]] si utilizzano i metodi Object.getPrototypeOf()Object.setPrototypeOf(). Questo è equivalente ad utilizzare la property __proto__, proprietà non-standard di JavaScript ma di fatto implementata da svaritati browser.

Non dev'essere confusa con la proprietà delle funzioni func.prototype, la quale invece specifica il [[Prototype]] da assegnare a tutte le istanze di oggetti creati dalla funzione data quando si usa un costruttore. La proprietà Object.prototype  rappresenta il prototipo oggetto  Object .

Di seguito viene mostrato cosa succede quando si tenta l'accesso ad una proprietà:

//Creiamo un oggetto o dalla funzione f con le sue properties a e b;
let f = function() {
   this.a = 1;
   this.b = 2;
}
let o = new f(); //{a: 1, b: 2}
//Aggiungiamo delle properties nel prototype di f
f.prototype.b = 3;
f.prototype.c = 4;

//non aggiungere le properties a f mediante f.prototype = {b:3,c:4}; questo romperebbe la prototype chain 
// o.[[Prototype]] ha le properties b and c:
// {b: 3, c: 4}
// o.[[Prototype]].[[Prototype]] è Object.prototype.
// Infine, o.[[Prototype]].[[Prototype]].[[Prototype]] é null.
// Questa è la fine della catena di prototipi poiché null, per defini// zione, non ha [[Prototype]].
// Così, l'intera catena di prototipi sarà:
// {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype ---> null

console.log(o.a); // 1
// C'è una property 'a' su o? Si, e il suo valore è 1.

console.log(o.b); // 2
// C'è una property 'b' su o? Si, e il suo valore è 2.
// Il prototype ha anche una property 'b', ma non è visitata. 
// Questa è chiamata "Property Shadowing"

console.log(o.c); // 4
// C'è una propria property 'c' su o? No, verifica il suo prototype.
// C'è una propria property 'c' su o.[[Prototype]]? si, il suo valore// è 4.

console.log(o.d); // undefined
// C'è una propria property 'd' su o? No, verifica il suo prototype.
// C'è una propria property 'd' su o.[[Prototype]]? No, verifica il suo prototype.
// o.[[Prototype]].[[Prototype]] è Object.prototype e non è presente nessuna property 'd' di default, verifica il suo prototype.
// o.[[Prototype]].[[Prototype]].[[Prototype]] è null, stop alla ricerca,
// nessuna property trovata, restituisce undefined

Impostando una property su un oggetto viene creata una own property. La sola eccezione alle regole di comportamento setting e getting è quando c'è una property ereditata con un getter or a setter.

"Metodi" ereditati

JavaScript non ha "metodi" nella forma tipica dei linguaggi basati sulle classi. In JavaScript, qualunque funzione può essere aggiunta ad un oggetto come fosse property. Una funzione ereditata agisce come ogni altra property, incluse le  property shadowing come mostrato di seguito (in questo caso, una forma di sovrascrittura di metodi).

Quando viene eseguita una funzione ereditata, il valore del this punta all'oggetto ereditante, non all'oggetto prototype dove la funzione è una property proprietaria (own property).

var o = {
  a: 2,
  m: function(b){
    return this.a + 1;
  }
};

console.log(o.m()); // 3
// Chiamando o.m in questo caso, 'this' si riferisce a o

var p = Object.create(o);
// p è un oggeto che eredita da o

p.a = 12; // crea una propria property 'a' su p
console.log(p.m()); // 13
// quando p.m è chiamata, 'this' si riferisce a p.
// Così quando p eredita la funzione m di o, 
// 'this.a' significa p.a, the propria property 'a' di p

L'uso di prototypes in Javascript

Guardiamo cos'è successo dietro le quinte con maggior dettaglio.

In Javascript, come già detto in precedenza, le funzioni possono avere delle properties. Tutte le funzioni hanno una property  speciale chiamata prototype. Si prega di notare che il codice sottostante è autonomo (si può presumere che non ci sia altro codice JavaScript nella pagina web oltre al codice sottostante). Per una migliore esperienza di apprendimento, si consiglia vivamente di aprire una console, navigare fino alla scheda "console", copiare e incollare nel codice JavaScript sottostante ed eseguirlo premendo il tasto Invio. (La console è inclusa nella maggior parte degli strumenti per sviluppatori del browser web. Ulteriori informazioni sono disponibili per Firefox Developer Tools, Chrome DevTools, and Edge DevTools.)

function doSomething(){}
console.log( doSomething.prototype );
// Non importa come dichiari una funzione, una
// funzione in JavaScript avrà sempre una property
// prototype di default.
var doSomething = function(){}; 
console.log( doSomething.prototype );

Come visto sopra, doSomething() ha una property prototype predefinita, come visualizzato dalla console. Dopo aver eseguito questo codice, la console dovrebbe aver visualizzato un oggetto simile a questo.

{
    constructor: ƒ doSomething(),
    __proto__: {
        constructor: ƒ Object(),
        hasOwnProperty: ƒ hasOwnProperty(),
        isPrototypeOf: ƒ isPrototypeOf(),
        propertyIsEnumerable: ƒ propertyIsEnumerable(),
        toLocaleString: ƒ toLocaleString(),
        toString: ƒ toString(),
        valueOf: ƒ valueOf()
    }
}

Possiamo aggiungere delle properties al prototype di doSomething(), come mostrato in seguito.

function doSomething(){}
doSomething.prototype.foo = "bar";
console.log( doSomething.prototype );

Il risultato è:

{
    foo: "bar",
    constructor: ƒ doSomething(),
    __proto__: {
        constructor: ƒ Object(),
        hasOwnProperty: ƒ hasOwnProperty(),
        isPrototypeOf: ƒ isPrototypeOf(),
        propertyIsEnumerable: ƒ propertyIsEnumerable(),
        toLocaleString: ƒ toLocaleString(),
        toString: ƒ toString(),
        valueOf: ƒ valueOf()
    }
}

Ora possiamo usare l'operatore new per creare un'istanza di doSomething() basata su questo prototipo. Per usare l'operatore new, è sufficiente invocare la funzione normalmente, utilizzando però il prefisso new. Chiamare una funzione con l'operatore new restituisce un oggetto che è un'istanza della funzione. Le proprietà possono poi essere aggiunte a questo oggetto.

Prova ad eseguire il seguente codice:

function doSomething(){}
doSomething.prototype.foo = "bar"; // aggiungo una property alla prototype
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // aggiungo una property all'oggetto
console.log( doSomeInstancing );

Il risultato restituito è simile al seguente:

{
    prop: "some value",
    __proto__: {
        foo: "bar",
        constructor: ƒ doSomething(),
        __proto__: {
            constructor: ƒ Object(),
            hasOwnProperty: ƒ hasOwnProperty(),
            isPrototypeOf: ƒ isPrototypeOf(),
            propertyIsEnumerable: ƒ propertyIsEnumerable(),
            toLocaleString: ƒ toLocaleString(),
            toString: ƒ toString(),
            valueOf: ƒ valueOf()
        }
    }
}

Come visto in precedenza, il __proto__ di doSomeInstancing è doSomething.prototype. Ma, cosa significa? Quando si accede a una proprietà di doSomeInstancing, il browser controlla se doSomeInstancing ha quella proprietà.

Se doSomeInstancing non ha la proprietà, allora il browser cerca la proprietà nel __proto__ di doSomeInstancing (alias doSomething.prototype). Se il __proto__ di doSomeInstancing ha la proprietà che si sta cercando, allora quella proprietà presente nel __proto__ di doSomeInstancing viene usata.

Altrimenti, se il __proto__ di doSomeInstancing non ha la proprietà, allora il __proto__ del __proto__ di doSomeInstancing viene controllato per verificare la presenza della proprietà cercata. Di default, il __proto__ di ogni proprietà prototype di qualsiasi funzione è window.Object.prototype. Quindi, il __proto__ del __proto__ di doSomeInstancing (alias il __proto__ di doSomething.prototype (alias Object.prototype))  viene poi controllato alla ricerca della proprietà che si sta cercando.

Se la proprietà non si trova nel __proto__ del __proto__ di doSomeInstancing, allora il __proto__ del __proto__ del __proto__ di doSomeInstancing viene esaminato. Tuttavia, c'è un problema: il __proto__ del __proto__ del __proto__  di doSomeInstancing non esiste. Quindi, e solo allora, dopo che l'intera catena di prototipi di __proto__ è stata esaminata, e non ci sono più __proto__, il browser conferma che la proprietà non esiste e conclude che il valore della proprietà è undefined.

Proviamo ad inserire altro codice nella console:

function doSomething(){}
doSomething.prototype.foo = "bar";
var 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);

Il risultato è il seguente:

doSomeInstancing.prop:      some value
doSomeInstancing.foo:       bar
doSomething.prop:           undefined
doSomething.foo:            undefined
doSomething.prototype.prop: undefined
doSomething.prototype.foo:  bar

Differenti modi di creare oggetti e la risultante catena di prototype

Oggetti creati con i costrutti sintattici

var o = {a: 1};

// L'oggetto o appena creato ha Object.prototype come proprio [[Prototype]]
// o non ha una propria property chamata 'hasOwnProperty'
// hasOwnProperty è una property propria di Object.prototype. 
// Quindi o eredita hasOwnProperty da Object.prototype
// Object.prototype ha null come suo prototype.
// o ---> Object.prototype ---> null

var a = ["yo", "whadup", "?"];

// Arrays ereditato da Array.prototype 
// (che metodi come indexOf, forEach, ecc.)
// La catena di prototype si presenta così:
// a ---> Array.prototype ---> Object.prototype ---> null

function f(){
  return 2;
}

// Le funzioni ereditano da Function.prototype 
// (che ha metodi come call, bind, ecc.)
// f ---> Function.prototype ---> Object.prototype ---> null

Con un constructor

Un "constructor" (costruttore) in JavaScript è semplicemente una funzione che è stata chiamata con  l'operatore new.

function Graph() {
  this.vertices = [];
  this.edges = [];
}

Graph.prototype = {
  addVertex: function(v){
    this.vertices.push(v);
  }
};

var g = new Graph();
// g è un oggetto con proprie properties 'vertices' ed 'edges'.
// g.[[Prototype]] è il valore di Graph.prototype quando viene eseguito new Graph().

Con Object.create

ECMAScript 5 introduce un nuovo metodo: Object.create(). Chiamando questo metodo viene creato un nuovo oggetto. Il prototype di questo oggetto è il primo argomento della funzione:

var a = {a: 1}; 
// a ---> Object.prototype ---> null

var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (ereditato)

var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null

var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty); 
// undefined, perché d non eredita da Object.prototype

Operatore delete con Object.create e l'operatore  new

L'operazione di cancellazione di una proprietà mediante l'utilizzo dell'operatore delete permette di evidenziare l'ereditarietà prototipale di un oggetto creato attraverso Object.create e l'oggetto utilizzato da prototipo del nuovo oggetto creato:

var a = {a: 1};

var b = Object.create(a); 

console.log(a.a); // stampa 1 
console.log(b.a); // stampa 1
b.a=5;
console.log(a.a); // stampa 1
console.log(b.a); // stampa 5
delete b.a;
console.log(a.a); // stampa 1
console.log(b.a); // stampa 1(b.a di valore 5 è cancellato ma viene visualizzato il valore contenuto nella prototype chain )
delete a.a;
console.log(a.a); // stampa undefined
console.log(b.a); // stampa undefined

L'operatore new ha una prototype chain più corta in questo esempio:

function Graph() {
  this.vertices = [4,4];
}

var g = new Graph();
console.log(g.vertices); // print [4,4]
g.vertices = 25;
console.log(g.vertices); // print 25
delete g.vertices;
console.log(g.vertices); // print undefined

Con la parola chiave class

ECMAScript 2015 introduce un nuovo gruppo di parole chiave per implementare le classi. Sebbene questi costrutti assomiglino a quelli familiari agli sviluppatori di linguaggi basati su classi, in realtà non cambia molto. JavaScript continua ad essere basato su prototype. Le nuove parole chiave includono class, constructor, static, extends, e super.

"use strict";

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

class Square extends Polygon {
  constructor(sideLength) {
    super(sideLength, sideLength);
  }
  get area() {
    return this.height * this.width;
  }
  set sideLength(newLength) {
    this.height = newLength;
    this.width = newLength;
  }
}

var square = new Square(2);

Prestazioni

Il tempo impiegato per la ricerca di proprietá che sono in alto nella catena dei prototype può avere un impatto negativo sulle prestazioni e questo può essere significativo in codice in cui le prestazioni sono critiche. Inoltre il tentativo di accedere a properties non esistenti esamina sempre la catena completa dei prototype.

In più, quando si itera sulle proprietà di un oggetto, tutte le properties enumerabili che si trovano nella sua catena dei prototype verranno enumerate. Per controllare se un oggetto ha una property definita da se stesso e non da qualche parte nella catena di prototype, è necessario utilizzare il metodo hasOwnProperty che tutti gli oggetti ereditano da Object.prototype. Per fornire un esempio concreto, analizziamo il codice del grafo visto in precedenza per illustrare quanto detto:

console.log(g.hasOwnProperty('vertices'));
// true

console.log(g.hasOwnProperty('nope'));
// false

console.log(g.hasOwnProperty('addVertex'));
// false

console.log(g.__proto__.hasOwnProperty('addVertex'));
// true

hasOwnProperty è la sola cosa in JavaScript che opera con le properties senza traversare la catena dei prototype.

Nota: non è sufficiente controllare se una property è undefined. La property potrebbe essere presente comunque e questo capita se il suo valore è stato assegnato undefined.

Cattiva pratica: Estensione di prototypes nativi

Una caratteristica mancante che viene spesso utilizzata è quella di estendere Object.prototype o uno degli altri prototype built-in.

Questa tecnica viene chiamata "monkey patching" e rompe l'incapsulazione. Nonostante sia utilizzata da frameworks popolari come ad esempio Prototype.js, non esistono comunque buone ragioni per appesantire i tipi built-in con funzionalità non-standard aggiuntive.

La sola buona ragione per estendere un prototype built-in è per dotare vecchie versioni di JavaScript con funzionalità presenti in quelle nuove; per esempio Array.forEach, etc.

Sommario dei metodi per l'estensione della prototype chain

Vengono presentati i 4 i metodi per l'estensione della prototype chain con i loro pro e i loro contro. Tutti gli esempi elencati di seguito creano esattamente lo stesso oggetto inst (ottenendo così gli stessi risultati nella console), ma in modi diversi allo scopo illustrativo.

Nome Esempi Pro Contro
Inizializzazione con New
function foo(){}
foo.prototype = {
  foo_prop: "foo val"
};
function bar(){}
var proto = new foo;
proto.bar_prop = "bar val";
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
Supportato in ogni browser immaginabile (il supporto va fino a IE 5.5!). Inoltre, è molto veloce, molto standard e molto JIST-optimizable. Per poter utilizzare questo metodo, la funzione in questione deve essere inizializzata. Durante questa inizializzazione, il costruttore può memorizzare informazioni uniche che devono essere generate per ogni oggetto. Tuttavia, queste informazioni uniche verrebbero generate una sola volta, il che potrebbe portare a problemi. Inoltre, l'inizializzazione del costruttore può inserire metodi indesiderati sull'oggetto. Tuttavia, queste criticità non sono generalmente problemi (in effetti, di solito sono utili) se si tratta di codice proprio e si sa cosa fa cosa e dove.
Object.create
function foo(){}
foo.prototype = {
  foo_prop: "foo val"
};
function bar(){}
var proto = Object.create(
  foo.prototype
);
proto.bar_prop = "bar val";
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
function foo(){}
foo.prototype = {
  foo_prop: "foo val"
};
function bar(){}
var proto = Object.create(
  foo.prototype,
  {
    bar_prop: {
      value: "bar val"
    }
  }
);
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop)
Supporto in tutti i browser in uso oggi, che sono tutti browser non microsoft più IE9 e successivi. Permette l'impostazione diretta di __proto__ in modo che il browser possa ottimizzare meglio l'oggetto. Permette anche la creazione di oggetti senza prototype tramite Object.create(null). Non supportato in IE8 e precedenti. Tuttavia, poiché Microsoft ha interrotto il supporto esteso per i sistemi che utilizzano questi vecchi browser, questo non dovrebbe essere un problema per la maggior parte delle applicazioni. Inoltre, l'inizializzazione lenta dell'oggetto può essere un buco nero per prestazioni se si usa il secondo argomento, perché ogni proprietà del descrittore dell'oggetto ha un proprio oggetto descrittore separato. Quando si ha a che fare con centinaia di migliaia di descrittori di oggetti sotto forma di oggetto, può sorgere un serio problema di ritardo.

Object.setPrototypeOf

function foo(){}
foo.prototype = {
  foo_prop: "foo val"
};
function bar(){}
var proto = {
  bar_prop: "bar val"
};
Object.setPrototypeOf(
  proto, foo.prototype
);
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
function foo(){}
foo.prototype = {
  foo_prop: "foo val"
};
function bar(){}
var proto;
proto=Object.setPrototypeOf(
  { bar_prop: "bar val" },
  foo.prototype
);
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop)
Supporto in tutti i browser in uso oggi, che sono tutti browser non microsoft più IE9 e successivi. Permette la manipolazione dinamica dei prototype degli oggetti e può anche forzare un prototype su un oggetto senza prototype creato con Object.create(null). Dovrebbe essere deprecato e poco performante. Far girare velocemente il vostro Javascript è completamente fuori questione se pensate di usarlo nel codice di produzione finale perché molti browser ottimizzano il prototype e cercano di indovinare la posizione del metodo nella memoria quando si chiama un'istanza in anticipo, ma impostando il prototype dinamicamente si interrompono tutte queste ottimizzazioni e si può anche forzare alcuni browser a ricompilare per la deottimizzazione il vostro codice solo per farlo funzionare secondo le specifiche. Non è supportato in IE8 e successivi.
__proto__
function foo(){}
foo.prototype = {
  foo_prop: "foo val"
};
function bar(){}
var proto = {
  bar_prop: "bar val",
  __proto__: foo.prototype
};
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
var inst = {
  __proto__: {
    bar_prop: "bar val",
    __proto__: {
      foo_prop: "foo val",
      __proto__: Object.prototype
    }
  }
};
console.log(inst.foo_prop);
console.log(inst.bar_prop)
Compatibilità con tutti i browser in uso oggi, che sono tutti browser non microsoft più IE11 e superiori. L'impostazione di __proto__ su qualcosa che non è un oggetto fallisce silenziosamente. Non lancia un'eccezione. Totalmente deprecato e non performante. Far girare velocemente il vostro codice Javascript è completamente fuori questione se pensate di usarlo nel codice di produzione finale perché molti browser ottimizzano il prototype e cercano di indovinare la posizione del metodo nella memoria quando si chiama un'istanza in anticipo, ma impostando il prototipo dinamicamente si interrompono tutte queste ottimizzazioni e si può anche forzare alcuni browser a ricompilare per la deottimizzazione del vostro codice solo per farlo funzionare secondo le specifiche. Non è supportato in IE10 e successivi.

prototype e Object.getPrototypeOf

JavaScript confonde un po' gli sviluppatori che provengono da Java o C++, essendo completamente dinamico, valutato tutto a runtime e non avendo classi in senso stretto. Sono tutte istanze di oggetti. Persino le "classi" che simuliamo sono semplicemente degli oggetti funzione.

Probabilmente hai già notato che la nostra funzione A ha una propery speciale chiamata prototype. Questa speciale property funziona con l'operatore new di JavaScript. Il riferimento all'oggetto prototype viene copiato nella property [[Prototype]] interna alla nuova istanza. Ad esempio, quando si fa var a1 = new A(), JavaScript (dopo aver creato l'oggetto in memoria e prima di eseguire la funzione A() con this definito ad esso) imposta a1. [[Prototype]] = A.prototype. Quando si accede poi alle properties dell'istanzaJavaScript prima controlla se esiste nell'oggetto direttamente e se non c'è guarda in [[Prototype]]. Questo significa che tutto ciò che viene definito in prototype viene effettivamente condiviso con tutte le istanze ed è possibile anche in seguito cambiare parti del prototype facendo comparire i cambiamenti in tutte le istanze esistenti, se è questo che si desidera.

Se, nell'esempio soprastante, si fa var a1 = new A(); var a2 = new A(); a1.doSomething farà riferimento a Object.getPrototypeOf(a1).doSomething, che è il medesimo di A.prototype.doSomething che è stato definito, perciò Object.getPrototypeOf(a1).doSomething == Object.getPrototypeOf(a2).doSomething == A.prototype.doSomething.

In breve, prototype è per i tipi, mentre Object.getPrototypeOf() da lo stesso risultato per le istanze.

[[Prototype]] viene esaminato ricorsivamente, ad esempio a1.doSomething, Object.getPrototypeOf(a1).doSomething, Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething ecc., finché viene trovato oppure Object.getPrototypeOf restituisce null.

Così, quando si chiama

var o = new Foo();

JavaScript in realtà esegue

var o = new Object();
o.[[Prototype]] = Foo.prototype;
Foo.call(o);

(o qualcosa di simile) e quando successivamente si esegue

o.someProp;

controlla se o ha pa property someProp. Se non c'è controlla Object.getPrototypeOf(o).someProp e se non c'è ancora controlla Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp e via di seguito.

In conclusione

È essenziale capire il funzionamento dell'ereditarietà basata sul modello dei prototype prima di scrivere codice complesso che ne fa uso. Bisogna anche fare attenzione alla lunghezza della catena di prototype nel proprio codice ed accorciarla in caso di necessità per evitare possibili problemi di prestazioni. Infine, i prototype nativi non dovrebbero mai venire estesi per evitare problemi di compatibilità con nuove funzionalità JavaScript che potrebbero essere introdotte.