Perché l'uso di costruttori è scoraggiato durante la creazione di prototipi?


10

Sfondo rapido: in JavaScript, la funzione di costruzione per ciascun tipo di oggetto ha una prototypeproprietà. Si prototyperiferisce a un oggetto che ciascun oggetto costruito utilizza come passaggio successivo nella catena del prototipo. Quando si desidera che un tipo sia inerente a un altro tipo, è possibile impostare il prototypetipo figlio su una nuova istanza del tipo padre.

Per esempio:

var Parent = function() { /* constructor business */ }
Parent.prototype.parentProp = "some parent property";

var Child = function() { /* constructor business */ }
Child.prototype = /*** !! Some prototype object goes here !! ***/

La mia domanda mi chiede su quale codice dovrebbe andare nel Some prototype object goes herepunto " " nel codice sopra. Il mio primo istinto è quello di costruire un'istanza del genitore (cioè, new Parent()), ma in un commento a una risposta su È un modo sicuro di copiare un prototipo di oggetti in un altro? , un utente scrive:

No, non utilizzare new bar()per l'oggetto prototipo!

(... che è un'opinione che ho visto in molte risposte e commenti SO, ma questo è l'unico esempio che ho a portata di mano al momento.)

L'altra opzione è usare Object.create(Parent.prototype)come Child.prototype. Per quanto ne so, questo crea anche una nuova Parentistanza, ma non esegue il Parentcostruttore.

Qualcuno può spiegare perché eseguire la funzione di costruzione dovrebbe essere evitato quando si genera un oggetto prototipo da un tipo genitore? Esiste qualche problema tecnico significativo (forse con livelli multipli di eredità)? O un tale schema è un uso improprio dei costruttori che si scontra con alcune best practice prototipiche (ad esempio, l'esecuzione del costruttore durante la creazione di un prototipo viola una certa separazione delle preoccupazioni)?

Risposte:


5

Perché è una funzione che non ha bisogno di essere chiamata. newnon è così diverso da una normale chiamata di funzione in Javascript.

Un costruttore può fare di più che semplicemente impostare i campi. Ad esempio, se convalida i dati in arrivo, causerai un errore di convalida quando stavi semplicemente cercando di impostare la catena di ereditarietà.

E non è necessario Object.create, questo è sufficiente:

function objectCreate( proto ) {
    function T(){}
    T.prototype = proto;
    return new T();
}

Ma è esattamente così che Object.createviene implementato.
Casey Chu,

@CaseyChu per niente. E l'ho menzionato perché nel post collegato qualcuno ha detto " Object.createnon funziona in IE8" - che è solo un commento inutile quando è possibile implementarlo per questo caso d'uso in 2 secondi su qualsiasi browser.
Esailija,

2

Ora che la mia comprensione si è ampliata un po ', vorrei basarmi sulla risposta di Esailija con un esempio specifico:

Una preoccupazione specifica è che un costruttore può impostare proprietà specifiche dell'istanza. Pertanto, se si utilizza un oggetto prototipo creato connew , tutte le istanze figlio potrebbero condividere dal prototipo un'unica proprietà specifica dell'istanza definita dal costruttore.

Ad esempio, supponiamo che Parentciascuna istanza abbia una idproprietà unica impostata come tempo di costruzione:

var Parent = function() { this.id = Math.random(); }
Parent.prototype.parentProp = "some parent property";

var Child = function() { /* constructor business */ }
Child.prototype = new Parent();

Ciò farà sì che tutte le Child istanze condividano una singola idproprietà del prototipo ereditata dall'unico e solo new Parent()oggetto usato comeChild.prototype .

Un approccio migliore per questo caso consiste nell'utilizzare Object.createe chiamare direttamente il costruttore principale (se necessario) all'interno del costruttore figlio:

var Parent = function() { this.id = Math.random(); }
Parent.prototype.parentProp = "some parent property";

var Child = function() {
    // run `this` through the Parent constructor function
    Parent.apply(this, arguments);
}
Child.prototype = Object.create(Parent.prototype);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.