Ereditarietà JavaScript: Object.create vs new


123

In JavaScript qual è la differenza tra questi due esempi:

Prerequisiti:

function SomeBaseClass(){
}

SomeBaseClass.prototype = {
    doThis : function(){
    },

    doThat : function(){
    }
}

Esempio di ereditarietà A utilizzando Object.create:

function MyClass(){
}

MyClass.prototype = Object.create(SomeBaseClass.prototype);

Esempio di ereditarietà B utilizzando la nuova parola chiave

function MyClass(){
}

MyClass.prototype = new SomeBaseClass();

Entrambi gli esempi sembrano fare la stessa cosa. Quando sceglieresti uno sull'altro?

Un'ulteriore domanda: considera il codice nel link sottostante (riga 15), dove un riferimento al costruttore della funzione è memorizzato nel prototipo. Perché è utile?

https://github.com/mrdoob/three.js/blob/master/src/loaders/ImageLoader.js

Estratto (se non vuoi aprire il link):

THREE.ImageLoader.prototype = {

    constructor: THREE.ImageLoader
}

23
Perché diavolo è contrassegnato come duplicato!?! L'altra domanda e le risposte non ne fanno nemmeno menzione Object.create. Questo è un errore e dovrebbe essere riaperto.
Scott Rippey


1
13 voti su quel commento e ancora non riaperto ..?!
TJ

Risposte:


111

Nella tua domanda hai detto che Both examples seem to do the same thing, non è affatto vero, perché

Il tuo primo esempio

function SomeBaseClass(){...}
SomeBaseClass.prototype = {
    doThis : function(){...},
    doThat : function(){...}
}
function MyClass(){...}
MyClass.prototype = Object.create(SomeBaseClass.prototype);

In questo esempio, stai solo ereditando, SomeBaseClass' prototypema cosa succede se hai una proprietà nel tuo SomeBaseClasssimile

function SomeBaseClass(){ 
    this.publicProperty='SomeValue'; 
}

e se lo usi come

var obj=new MyClass();
console.log(obj.publicProperty); // undefined
console.log(obj);​

L' objoggetto non avrà publicPropertyproprietà come in questo esempio .

Il tuo secondo esempio

MyClass.prototype = new SomeBaseClass();

Sta eseguendo la constructorfunzione, creando un'istanza SomeBaseClassed ereditando l'intero SomeBaseClassoggetto. Quindi, se usi

    var obj=new MyClass();
    console.log(obj.publicProperty); // SomeValue
    console.log(obj);​

In questo caso la sua publicPropertyproprietà è disponibile anche per l' objoggetto come in questo esempio .

Poiché Object.createnon è disponibile in alcuni vecchi browser, in tal caso è possibile utilizzare

if(!Object.create)
{
    Object.create=function(o){
        function F(){}
        F.prototype=o;
        return new F();
    }
}

Il codice sopra aggiunge solo la Object.createfunzione se non è disponibile, quindi puoi usare la Object.createfunzione e penso che il codice sopra descriva cosa Object.createfa effettivamente. Spero che ti aiuti in qualche modo.


6
Ciao Sheikh. Grazie per i tuoi sforzi su questo. Sì, la differenza è che il costruttore viene eseguito nel secondo esempio ma non nel primo. (che è desiderabile nel mio caso). Per quanto riguarda la proprietà pubblica non definita che non viene ereditata dall'implementazione super, è sufficiente chiamare super nel costruttore del bambino: SomeBaseClass.call (this). Controlla questo violino: jsfiddle.net/NhQGB
ChrisRich

Ho cercato un modo semplicissimo per l'ereditarietà corretta in JS senza utilizzare librerie / framework. Penso che questo esempio (nel mio violino sopra) sia l'approccio migliore per i browser moderni. Forse il polyfill Object.Create potrebbe aggiungere il supporto per i browser legacy?
ChrisRich

quindi, in pratica, per riassumere, se fai .prototype = new allora stai ereditando i valori che hai assegnato a questo nella classe base, e quando fai object.create stai ereditando SOLO ciò che è sul prototipo nella classe base, giusto?
davidjnelson

Questo "MyClass.prototype = new SomeBaseClass ()" significa che la nuova istanza di SomeBaseClass viene creata quando l'istanza di MyClass non è ancora stata creata?

No, solo quando crei l'oggetto principale il prototipo è impostato su quello SomeBaseClass.
L'Alfa

39

Entrambi gli esempi sembrano fare la stessa cosa.

Questo è vero nel tuo caso.

Quando sceglieresti uno sull'altro?

quando SomeBaseClass ha un corpo di funzione, questo verrebbe eseguito con la newparola chiave. Questo di solito non è previsto: si desidera solo impostare la catena del prototipo. In alcuni casi, potrebbe anche causare seri problemi perché si crea effettivamente un'istanza di un oggetto, le cui variabili con ambito privato sono condivise da tutte le MyClassistanze poiché ereditano gli stessi metodi privilegiati. Altri effetti collaterali sono immaginabili.

Quindi, in genere dovresti preferire Object.create. Tuttavia, non è supportato in alcuni browser legacy; questo è il motivo per cui vedi l' newapproccio troppo frequente poiché spesso non fa (ovvio) danno. Dai un'occhiata anche a questa risposta .


Questa è la risposta migliore, ben spiegata
spex

8

La differenza diventa evidente se si utilizza Object.create()come previsto. In realtà, nasconde completamente la prototypeparola dal tuo codice, farà il lavoro sotto il cofano. Usando Object.create(), possiamo andare come

var base =  {
    doThis : function(){
    },

    doThat : function(){
    }
};

E poi possiamo estendere / ereditare altri oggetti da questo

var myObject = Object.create( base );
// myObject will now link to "base" via the prototype chain internally

Quindi questo è un altro concetto, un modo di ereditare più "orientato agli oggetti". Ad esempio, non esiste una "funzione costruttore" pronta all'uso Object.create(). Ma ovviamente potresti semplicemente creare e chiamare una funzione di costruzione auto definita all'interno di quegli oggetti.

Un argomento per l'utilizzo Object.create()è che potrebbe sembrare più naturale per miscelare / * * ereditare da altri oggetti, che usare Javascripts modo predefinito .


7
Object.createnon può davvero sostituire l'approccio classico con newquando è necessaria una funzione di costruzione
Bergi

1
Sicuramente può @Bergi. Guarda questo post del blog: davidwalsh.name/javascript-objects-deconstruction . È inoltre possibile passare un oggetto di inizializzazione come secondo parametro a Object.create ().
LocalPCGuy

@LocalPCGuy: No, non può, perché Object.createnon chiama una funzione che sarebbe necessaria per creare chiusure. Naturalmente, con Object.createpuoi mettere un initmetodo sui tuoi oggetti e chiamarlo immediatamente e forse anche restituire in thismodo da ottenere una sintassi concisa - ma aspetta, allora quello è un costruttore.
Bergi

1
La chiamata a una funzione di inizializzazione funziona in modo simile a un costruttore, ma non è un costruttore. E quel metodo ti consente di sostituire esattamente l'approccio classico con Object.create. E il modello mentale del collegamento tra oggetti è molto più semplice di quello creato tramite "nuovo".
LocalPCGuy

Ebbene, il mio modello mentale newutilizza il "collegamento tra oggetti", quindi non c'è molta differenza. In effetti, ci sono solo due cose: .constructorviene chiamata .inite quella funzione non ha una .prototypeproprietà che rimandi al tuo oggetto prototipo. Il resto è solo sintassi - e io preferisco new Xoltre Object.create(x).init().
Bergi

0

Non sono un esperto di java script, ma ecco un semplice esempio per capire la differenza tra "Object.create" e "new" ..

passaggio 1: crea la funzione genitore con alcune proprietà e azioni ..

function Person() {

this.name = 'venkat';

this.address = 'dallas';

this.mobile='xxxxxxxxxx'

}

Person.prototype.func1 = function () {

    return this.name + this.address;
}

passaggio 2: crea una funzione figlio (PersonSalary) che si estende sopra la funzione Persona utilizzando la nuova parola chiave.

function PersonSalary() {
    Person.call(this);
}
PersonSalary.prototype = new Person();

PersonSalary();

passaggio 3: crea la seconda funzione figlio (PersonLeaves) che si estende sopra la funzione Person utilizzando la parola chiave Object.create ..

function PersonLeaves() {
 Person.call(this);
}
PersonLeaves.prototype = Object.create(Person.prototype);


PersonLeaves();

// Ora controlla entrambi i prototipi di funzioni figlio.

PersonSalary.prototype
PersonLeaves.prototype

entrambe queste funzioni figlie si collegheranno al prototipo Person (funzione genitore) e potranno accedere ai suoi metodi ma se crei una funzione figlia usando new restituirà un oggetto nuovo di zecca con tutte le proprietà genitore di cui non abbiamo bisogno e anche quando ne crei oggetto o funzione utilizzando "Nuovo" viene eseguita quella funzione genitore che non vogliamo essere.

Ecco i piatti da asporto

se vuoi solo delegare ad alcuni metodi nella funzione genitore e non vuoi che venga creato un nuovo oggetto, usare Object.create è il modo migliore.

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.