2010-11-12 20 views
308

Recentemente sono incappato nel metodo Object.create() in JavaScript e sto cercando di dedurre come è diverso dalla creazione di una nuova istanza di un oggetto con new SomeFunction() e quando si vorrebbe usare l'uno sull'altro.Comprendere la differenza tra Object.create() e new SomeFunction()

consideri il seguente esempio:

var test = { 
 
    val: 1, 
 
    func: function() { 
 
    return this.val; 
 
    } 
 
}; 
 
var testA = Object.create(test); 
 

 
testA.val = 2; 
 
console.log(test.func()); // 1 
 
console.log(testA.func()); // 2 
 

 
console.log('other test'); 
 
var otherTest = function() { 
 
    this.val = 1; 
 
    this.func = function() { 
 
    return this.val; 
 
    }; 
 
}; 
 

 
var otherTestA = new otherTest(); 
 
var otherTestB = new otherTest(); 
 
otherTestB.val = 2; 
 
console.log(otherTestA.val); // 1 
 
console.log(otherTestB.val); // 2 
 

 
console.log(otherTestA.func()); // 1 
 
console.log(otherTestB.func()); // 2

Si noti che lo stesso comportamento si osserva in entrambi i casi. Mi sembra che le principali differenze tra questi due scenari sono:

  • L'oggetto utilizzato in Object.create() costituisce in realtà il prototipo del nuovo oggetto, mentre nel new Function() dalle proprietà dichiarate/funzioni non formano il prototipo.
  • Non è possibile creare chiusure con la sintassi Object.create() come con la sintassi funzionale. Questo è logico dato lo scope di tipo lessicale (vs block) di JavaScript.

Le precedenti istruzioni sono corrette? E mi manchi qualcosa? Quando useresti l'uno sull'altro?

EDIT: link alla versione jsfiddle di codice di esempio sopra: http://jsfiddle.net/rZfYL/

+2

Vedere anche [Utilizzo di "Object.create" invece di "nuovo"] (http: // stackoverflow.com/q/2709612/1048572) – Bergi

risposta

205

L'oggetto utilizzato in Object.create costituisce in realtà il prototipo del nuovo oggetto, dove come nella nuova funzione Function() le proprietà/funzioni dichiarate non formano il prototipo.

Sì, Object.create crea un oggetto che eredita direttamente da quello passato come primo argomento.

Con funzioni di costruzione, l'oggetto appena creato eredita dal prototipo del costruttore, es .:

var o = new SomeConstructor(); 

Nell'esempio precedente, o eredita direttamente da SomeConstructor.prototype.

C'è una differenza qui, con Object.create è possibile creare un oggetto che non eredita da qualsiasi cosa, Object.create(null);, d'altra parte, se si imposta SomeConstructor.prototype = null; l'oggetto appena creato eredita da Object.prototype.

Non è possibile creare chiusure con la sintassi Object.create come si farebbe con la sintassi funzionale. Questo è logico dato lo scope di tipo lessicale (vs block) di JavaScript.

Bene, è possibile creare chiusure, ad es.utilizzando descrittori di proprietà argomento:

var o = Object.create({inherited: 1}, { 
    foo: { 
    get: (function() { // a closure 
     var closured = 'foo'; 
     return function() { 
     return closured+'bar'; 
     }; 
    })() 
    } 
}); 

o.foo; // "foobar" 

Nota che sto parlando del ECMAScript 5th Edition Object.create metodo, non lo spessore del Crockford.

Il metodo sta iniziando a essere implementato in modo nativo sugli ultimi browser, controllare questo compatibility table.

+1

@CMS 2 domande. 1) La catena di portata su Object.create (null) termina ancora nell'ambito globale (come 'finestra' in un browser) o termina su se stessa? 2) Non mi è ancora chiaro il motivo per cui è stato introdotto Object.create (ad esempio, quale caratteristica mancava a questo indirizzo?) E perché si userebbe invece di new Function(); – Matt

+8

@Matt, 1) qui la catena di scope non è un concetto correlato, la catena di portata è correlata alla ** risoluzione dell'identificatore **, ad esempio: come 'foo;' è risolto nell'attuale * ambiente lessicale *. 2) Fornire un modo semplice per implementare l'ereditarietà, è un costrutto davvero potente. IMO Lo userei perché è davvero semplice e leggero, ma per il codice di produzione, dobbiamo ancora aspettare un po 'di tempo prima che ES5 sia ampiamente supportato. Per quanto riguarda le funzionalità mancanti, mancava il fatto di creare un oggetto "pristine", 'Object.create (null);' è davvero utile implementare oggetti hash-table-like affidabili ... – CMS

+0

@CMS Grazie! – Matt

7

Internamente Object.create fa questo:

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

La sintassi vuole solo via l'illusione che JavaScript utilizza eredità classica.

+24

Il metodo ECMAScript 5 ['Object.create'] (http://sideshowbarker.github.com/es5-spec/#x15.2.3.5), fa molto di più, puoi definire le proprietà per * descrittori di proprietà * ed è possibile creare un oggetto che non eredita da qualsiasi cosa ('Object.create (null);'), questo tipo di shim dovrebbe essere evitato perché non si può davvero emulare quel comportamento su ES3. [Maggiori informazioni] (http://stackoverflow.com/questions/3830800/object-defineproperty-in-es5/3844768#3844768) – CMS

+0

Hmm, non lo sapeva. – xj9

+0

D'accordo con @CMS ma, in generale, è semplice polyfill per 'Object.create'. –

166

Ecco i passaggi che avvengono internamente per entrambe le chiamate:
(suggerimento: l'unica differenza è nel passaggio 3)


new Test():

  1. creare new Object() obj
  2. set obj.__proto__ a Test.prototype
  3. return Test.call(obj) || obj; // normally obj is returned but constructors in JS can return a value

Object.create(Test.prototype)

  1. creare new Object() obj
  2. impostato obj.__proto__ al Test.prototype
  3. return obj;

Quindi, in pratica, Object.create non esegue il costruttore.

+4

Confronto molto utile. –

+0

@Ray in modo che usando object.create noi font abbiamo le proprietà della funzione menzionata nella funzione del costruttore? –

+0

@sortednoun fintanto che le proprietà sono private e non specificate sul prototipo, ** sì, non saranno ereditate e non le avrai nel nuovo oggetto ** (e, vorrei aggiungere, puoi aspettarti per ottenere eventuali proprietà prototipate dal genitore, solo quando il costruttore genitore è stato eseguito almeno una volta). – Kamafeather

16

La differenza è la cosiddetta "ereditarietà pseudoclassica vs. prototipo". Il suggerimento è di usare solo un tipo nel tuo codice, non mescolare i due.

Nell'ereditarietà pseudoclassica (con operatore "new"), immagina di definire prima una pseudo-classe e quindi creare oggetti da quella classe. Ad esempio, definire una "Persona" pseudo-classe, quindi creare "Alice" e "Mario" da "Persona".

Nell'ereditarietà prototipale (utilizzando Object.create), si crea direttamente una persona specifica "Alice", quindi si crea un'altra persona "Bob" utilizzando "Alice" come prototipo. Non c'è "classe" qui; tutti sono oggetti.

All'interno, JavaScript utilizza "ereditarietà prototipale"; il modo "pseudoclassico" è solo un po 'di zucchero.

Vedere this link per un confronto dei due modi.

+0

Anche questa risposta è buona. +1 – Bento

297

Molto semplicemente detto, new X è Object.create(X.prototype) con in aggiunta la funzione constructor. (E dando il constructor la possibilità di return l'oggetto effettivo che dovrebbe essere il risultato dell'espressione invece di this.)

Questo è tutto. :)

Il resto delle risposte è solo confuso, perché a quanto pare nessun altro legge la definizione di new.;)

+18

+1 Semplicità e chiarezza! (Anche se Object.create (null) sembra una buona opzione - forse dovrebbe menzionarlo). – user949300

+1

La migliore risposta che ho visto. Estremamente utile! – m0meni

+0

mantieni semplice la strada da percorrere – Bruce

20
function Test(){ 
    this.prop1 = 'prop1'; 
    this.prop2 = 'prop2'; 
    this.func1 = function(){ 
     return this.prop1 + this.prop2; 
    } 
}; 

Test.prototype.protoProp1 = 'protoProp1'; 
Test.prototype.protoProp2 = 'protoProp2'; 
var newKeywordTest = new Test(); 
var objectCreateTest = Object.create(Test.prototype); 

/* Object.create */ 
console.log(objectCreateTest.prop1); // undefined 
console.log(objectCreateTest.protoProp1); // protoProp1 
console.log(objectCreateTest.__proto__.protoProp1); // protoProp1 

/* new */ 
console.log(newKeywordTest.prop1); // prop1 
console.log(newKeywordTest.__proto__.protoProp1); // protoProp1 

Sommario:

1) con new parola chiave ci sono due cose da notare;

a) funzione viene usato come un costruttore

b) function.prototype oggetto viene passato alla proprietà __proto__ ... o dove __proto__ non è supportato, è il secondo luogo in cui il nuovo oggetto sembra trovare immobili

2) con Object.create(obj.prototype) si sta costruendo un oggetto (obj.prototype) e passando alla oggetto destinato ..con la differenza che ora nuovo oggetto di __proto__ è anche indicando per obj.prototype (si prega di ref ans da XJ9 per questo)

31

Questo:

var foo = new Foo(); 

e

var foo = Object.create(Foo.prototype); 

sono molto simili. Una differenza importante è che in realtà new Foo corre codice del costruttore, mentre Object.create non verrà eseguire codice come

function Foo() { 
    alert("This constructor does not run with Object.create"); 
} 

Si noti che se si utilizza la versione a due parametri di Object.create() allora si può fare cose molto più potenti.

+1

Grande spiegazione. Potrei aggiungere, usando 'Object.create' nella sua forma più semplice come questa, che ti consente di omettere le funzioni di costruzione dal tuo codice, sfruttando al contempo l'ereditarietà del prototipo. –

51

Fammi provare a spiegare (più su Blog):

  1. Quando si scrive Car costruttore var Car = function(){}, questo è come stanno le cose internamente: A diagram of prototypal chains when creating javascript objects ne abbiamo una {prototype} link nascosto al Function.prototype che non è accessibile e un collegamento prototype a Car.prototype che è accessibile e ha un numero effettivo constructor di Car. Sia Function.prototype che Car.prototype hanno collegamenti nascosti a Object.prototype.
  2. Quando vogliamo creare due oggetti equivalenti utilizzando l'operatore new e il metodo create, dobbiamo farlo in questo modo: Honda = new Car(); e Maruti = Object.create(Car.prototype). A diagram of prototypal chains for differing object creation methods Cosa sta succedendo?

    Honda = new Car(); — Quando si crea un oggetto come questo poi nascosto {prototype} proprietà sia puntato Car.prototype. Quindi qui, lo {prototype} dell'oggetto Honda sarà sempre Car.prototype — non abbiamo alcuna opzione per modificare la proprietà {prototype} dell'oggetto. Cosa succede se voglio cambiare il prototipo del nostro oggetto appena creato?
    Maruti = Object.create(Car.prototype) — Quando si crea un oggetto come questo, è disponibile un'opzione aggiuntiva per scegliere la proprietà {prototype} dell'oggetto. Se si desidera Car.prototype come {prototype}, passarlo come parametro nella funzione.Se non vuoi alcun {prototype} per il tuo oggetto allora puoi passare null in questo modo: Maruti = Object.create(null).

Conclusione — Utilizzando il metodo Object.create si ha la libertà di scegliere la vostra proprietà oggetto {prototype}. In new Car();, non hai quella libertà.

modo preferito in OO JavaScript:

Supponiamo di avere due oggetti a e b.

var a = new Object(); 
var b = new Object(); 

Ora, supponiamo a ha alcuni metodi che b vuole anche accedere. Per questo, abbiamo bisogno di ereditarietà degli oggetti (a dovrebbe essere il prototipo di b solo se vogliamo accedere a questi metodi). Se controlliamo i prototipi di a e b, scopriremo che condividono il prototipo Object.prototype.

Object.prototype.isPrototypeOf(b); //true 
a.isPrototypeOf(b); //false (the problem comes into the picture here). 

Problema — vogliamo obiettare a come il prototipo di b, ma qui abbiamo creato oggetto b con il prototipo Object.prototype. Soluzione — ECMAScript 5 ha introdotto Object.create(), per ottenere facilmente tale eredità. Se creiamo oggetto b come questo:

var b = Object.create(a); 

poi,

a.isPrototypeOf(b);// true (problem solved, you included object a in the prototype chain of object b.) 

Quindi, se si sta facendo di oggetti script orientato quindi Object.create() è molto utile per l'eredità.

+0

Buono. Penso che questa sia la risposta che colpisce gli occhi di bue – Bento

+0

Quindi, è in qualche modo simile alla creazione di oggetti senza invocazione del costruttore? Ci godremo tutti i benefici della classe. Anche l'obj instanceof Class sarà vera. Ma non stiamo invocando la funzione Class tramite new. – Praveen

+0

@Anshul Hai detto che 'a.isPrototypeOf (b);' restituirà 'false' che è giusto, perché entrambi gli oggetti sono diversi e puntano a una memoria diversa. Il modo corretto di farlo con l'operatore 'new' è qui. - https://jsfiddle.net/167onunp/. –

3

conseguenza per this answer e this videonew parola chiave fa cose prossimi:

  1. Crea nuovo oggetto.

  2. Collega nuovo oggetto alla funzione di costruzione (prototype).

  3. Rende il punto variabile this al nuovo oggetto.

  4. Esegue la funzione di costruzione utilizzando il nuovo oggetto e esegue implicitamente return this;

  5. Assegna il nome della funzione di costruzione alla proprietà del nuovo oggetto constructor.

Object.create esegue solo 1st e 2nd passi !!!

Problemi correlati