Usare “Object.create” invece di “new”

Javascript 1.9.3 / ECMAScript 5 introduce Object.create , che Douglas Crockford, tra gli altri, difende da molto tempo. Come sostituisco new nel codice qui sotto con Object.create ?

 var UserA = function(nameParam) { this.id = MY_GLOBAL.nextId(); this.name = nameParam; } UserA.prototype.sayHello = function() { console.log('Hello '+ this.name); } var bob = new UserA('bob'); bob.sayHello(); 

(Supponiamo che MY_GLOBAL.nextId esista).

Il meglio che posso inventare è:

 var userB = { init: function(nameParam) { this.id = MY_GLOBAL.nextId(); this.name = nameParam; }, sayHello: function() { console.log('Hello '+ this.name); } }; var bob = Object.create(userB); bob.init('Bob'); bob.sayHello(); 

Non sembra esserci alcun vantaggio, quindi penso di non averlo capito. Probabilmente sono troppo neoclassico. Come dovrei usare Object.create per creare user ‘bob’?

    Con un solo livello di ereditarietà, il tuo esempio potrebbe non farti vedere i reali benefici di Object.create .

    Questo metodo consente di implementare facilmente l’ ereditarietà differenziale , in cui gli oggetti possono ereditare direttamente da altri oggetti.

    Nell’esempio userB , non penso che il tuo metodo init debba essere pubblico o addirittura esistere, se chiami ancora questo metodo su un’istanza di object esistente, le proprietà id e name cambieranno.

    Object.create consente di inizializzare le proprietà dell’object utilizzando il suo secondo argomento, ad esempio:

     var userB = { sayHello: function() { console.log('Hello '+ this.name); } }; var bob = Object.create(userB, { 'id' : { value: MY_GLOBAL.nextId(), enumerable:true // writable:false, configurable(deletable):false by default }, 'name': { value: 'Bob', enumerable: true } }); 

    Come puoi vedere, le proprietà possono essere inizializzate sul secondo argomento di Object.create , con un object letterale che utilizza una syntax simile a quella utilizzata dai metodi Object.defineProperties e Object.defineProperty .

    Ti consente di impostare gli attributi di proprietà ( enumerable , writable o configurable ), che possono essere davvero utili.

    Non c’è davvero alcun vantaggio nell’utilizzare Object.create(...) sul new object .

    Coloro che sostengono questo metodo indicano in genere vantaggi piuttosto ambigui: “scalabilità” , o ” più naturale per JavaScript ” ecc.

    Tuttavia, devo ancora vedere un esempio concreto che mostra che Object.create presenta vantaggi rispetto all’uso di new . Al contrario ci sono problemi noti con esso. Sam Elsamman descrive cosa succede quando ci sono oggetti nidificati e Object.create(...) è usato :

     var Animal = { traits: {}, } var lion = Object.create(Animal); lion.traits.legs = 4; var bird = Object.create(Animal); bird.traits.legs = 2; alert(lion.traits.legs) // shows 2!!! 

    Ciò si verifica perché Object.create(...) sostiene una pratica in cui i dati vengono utilizzati per creare nuovi oggetti; qui il dato Animal diventa parte del prototipo di lion e bird e causa problemi come è condiviso. Quando si utilizza new l’ereditarietà del prototipo è esplicita:

     function Animal() { this.traits = {}; } function Lion() { } Lion.prototype = new Animal(); function Bird() { } Bird.prototype = new Animal(); var lion = new Lion(); lion.traits.legs = 4; var bird = new Bird(); bird.traits.legs = 2; alert(lion.traits.legs) // now shows 4 

    Per quanto riguarda gli attributi di proprietà facoltativi passati in Object.create(...) , questi possono essere aggiunti utilizzando Object.defineProperties(...) .

    Object.create non è ancora standard su diversi browser, ad esempio IE8, Opera v11.5, Konq 4.3 non ce l’ha. Puoi usare la versione di ObjectCreate di Douglas Crockford per quei browser, ma questo non include il secondo parametro “object di inizializzazione” usato nella risposta di CMS.

    Per il codice cross browser, un modo per ottenere l’inizializzazione dell’object nel frattempo è la personalizzazione di Object.create di Crockford. Ecco un metodo: –

     Object.build = function(o) { var initArgs = Array.prototype.slice.call(arguments,1) function F() { if((typeof o.init === 'function') && initArgs.length) { o.init.apply(this,initArgs) } } F.prototype = o return new F() } 

    Ciò mantiene l’ereditarietà prototipale di Crockford, e controlla anche qualsiasi metodo init nell’object, quindi lo esegue con i parametri, come ad esempio new man (‘John’, ‘Smith’). Il tuo codice diventa quindi: –

     MY_GLOBAL = {i: 1, nextId: function(){return this.i++}} // For example var userB = { init: function(nameParam) { this.id = MY_GLOBAL.nextId(); this.name = nameParam; }, sayHello: function() { console.log('Hello '+ this.name); } }; var bob = Object.build(userB, 'Bob'); // Different from your code bob.sayHello(); 

    Quindi Bob eredita il metodo sayHello e ora ha proprietà proprie id = 1 e name = ‘Bob’. Queste proprietà sono sia scrivibili che enumerabili, naturalmente. Questo è anche un modo molto più semplice per inizializzare rispetto a ECMA Object.create, specialmente se non si è preoccupati degli attributi scrivibili, enumerabili e configurabili.

    Per l’inizializzazione senza un metodo init è ansible utilizzare il seguente mod Crockford: –

     Object.gen = function(o) { var makeArgs = arguments function F() { var prop, i=1, arg, val for(prop in o) { if(!o.hasOwnProperty(prop)) continue val = o[prop] arg = makeArgs[i++] if(typeof arg === 'undefined') break this[prop] = arg } } F.prototype = o return new F() } 

    Questo riempie le proprietà dell’utente dell’utente B, nell’ordine in cui sono definite, utilizzando i parametri Object.gen da sinistra a destra dopo il parametro userB. Utilizza il ciclo for (prop in o) così, per gli standard ECMA, l’ordine di enumerazione delle proprietà non può essere garantito allo stesso modo dell’ordine di definizione della proprietà. Tuttavia, diversi esempi di codice testati su (4) i principali browser mostrano che sono uguali, a condizione che venga utilizzato il filtro hasOwnProperty e talvolta anche se non lo sia.

     MY_GLOBAL = {i: 1, nextId: function(){return this.i++}}; // For example var userB = { name: null, id: null, sayHello: function() { console.log('Hello '+ this.name); } } var bob = Object.gen(userB, 'Bob', MY_GLOBAL.nextId()); 

    Direi piuttosto che Object.build dal momento che userB non ha bisogno di un metodo init. Anche userB non è specificamente un costruttore ma sembra un normale object singleton. Quindi con questo metodo puoi build e inizializzare da normali oggetti semplici.

    TL; DR:

    new Computer() invocherà la funzione di costruzione Computer(){} per una volta, mentre Object.create(Computer.prototype) non lo farà.

    Tutti i vantaggi si basano su questo punto.

    Sebbene per alcuni motivi di ottimizzazione del motore interno, Object.create potrebbe essere più lento, il che non è intuitivo.

    Potresti fare in modo che il metodo init restituisca this , e quindi concatenare le chiamate, in questo modo:

     var userB = { init: function(nameParam) { this.id = MY_GLOBAL.nextId(); this.name = nameParam; return this; }, sayHello: function() { console.log('Hello '+ this.name); } }; var bob = Object.create(userB).init('Bob'); 

    Un altro ansible utilizzo di Object.create è quello di clonare oggetti immutabili in modo economico ed efficace .

     var anObj = { a: "test", b: "jest" }; var bObj = Object.create(anObj); bObj.b = "gone"; // replace an existing (by masking prototype) bObj.c = "brand"; // add a new to demonstrate it is actually a new obj // now bObj is {a: test, b: gone, c: brand} 

    Note : il frammento di cui sopra crea un clone di un object sorgente (noto anche come riferimento, come in cObj = aObj). Beneficia del metodo copy-properties (vedi 1 ), in quanto non copia le proprietà dei membri dell’object. Piuttosto crea un altro object -destinazione- con il suo prototipo impostato sull’object sorgente. Inoltre, quando le proprietà vengono modificate sull’object dest, vengono create “al volo”, mascherando le proprietà del prototipo (src). Questo costituisce un modo veloce ed efficace per clonare oggetti immutabili.

    L’avvertimento qui è che questo si applica agli oggetti sorgente che non dovrebbero essere modificati dopo la creazione (immutabile). Se l’object sorgente viene modificato dopo la creazione, anche tutte le proprietà non mascherate del clone saranno modificate.

    Fiddle qui ( http://jsfiddle.net/y5b5q/1/ ) (ha bisogno del browser capace di Object.create).

    A volte non è ansible creare un object con NEW ma è ancora ansible richiamare il metodo CREATE.

    Ad esempio: se si desidera definire un elemento personalizzato deve derivare da HTMLElement.

     proto = new HTMLElement //fail :( proto = Object.create( HTMLElement.prototype ) //OK :) document.registerElement( "custom-element", { prototype: proto } ) 

    Penso che il punto principale in questione sia capire la differenza tra approcci new e Object.create . Di conseguenza a questa risposta e a questa new parola chiave video fa le cose seguenti:

    1. Crea un nuovo object.

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

    3. Rende this punto variabile al nuovo object.

    4. Esegue la funzione di costruzione utilizzando il nuovo object ed esegue implicitamente return this ;

    5. Assegna il nome della funzione di costruzione al constructor della proprietà di un nuovo object.

    Object.create esegue solo i passaggi 1st e 2nd !!!

    Nell’esempio di codice fornito in questione non è un grosso problema, ma nel prossimo esempio è:

     var onlineUsers = []; function SiteMember(name) { this.name = name; onlineUsers.push(name); } SiteMember.prototype.getName = function() { return this.name; } function Guest(name) { SiteMember.call(this, name); } Guest.prototype = new SiteMember(); var g = new Guest('James'); console.log(onlineUsers); 

    Come risultato secondario il risultato sarà:

     [ undefined, 'James' ] 

    a causa di Guest.prototype = new SiteMember();
    Ma non abbiamo bisogno di eseguire il metodo del costruttore genitore, dobbiamo solo rendere il metodo getName disponibile in Guest. Quindi dobbiamo usare Object.create .
    Se sostituisci Guest.prototype = new SiteMember();
    to Guest.prototype = Object.create(SiteMember.prototype); risultato essere:

     [ 'James' ] 

    Il vantaggio è che Object.create è in genere più lento di new sulla maggior parte dei browser

    In questo esempio di jsperf , in un Chromium, il browser new è 30 volte più veloce di Object.create(obj) sebbene entrambi siano abbastanza veloci. Questo è tutto piuttosto strano perché il nuovo fa più cose (come invocare un costruttore) dove Object.create dovrebbe semplicemente creare un nuovo object con l’object passato come prototipo (link segreto in Crockford-speak)

    Forse i browser non sono riusciti a rendere Object.create più efficiente (forse lo stanno basando su new sotto le copertine … anche nel codice nativo)

    Devi creare una funzione Object.create() . Uno che risolva i problemi di Crockfords e chiama anche la funzione init.

    Questo funzionerà:

     var userBPrototype = { init: function(nameParam) { this.name = nameParam; }, sayHello: function() { console.log('Hello '+ this.name); } }; function UserB(name) { function F() {}; F.prototype = userBPrototype; var f = new F; f.init(name); return f; } var bob = UserB('bob'); bob.sayHello(); 

    Qui UserB è come Object.create, ma adattato alle nostre esigenze.

    Se vuoi, puoi anche chiamare:

     var bob = new UserB('bob'); 

    Mentre Douglas Crockford era un sostenitore zelante di Object.create () ed è fondamentalmente il motivo per cui questo costrutto è in realtà in javascript, non ha più questa opinione.

    Ha smesso di usare Object.create, perché ha smesso di usare questa parola chiave del tutto perché causa troppi problemi. Ad esempio, se non si presta attenzione, può facilmente puntare all’object globale, che può avere conseguenze davvero negative. E sostiene che senza usare questo Object.create non ha più senso.

    Puoi controllare questo video dal 2014 dove parla con Nordic.js:

    https://www.youtube.com/watch?v=PSGEjv3Tqo0

    inserisci la descrizione dell'immagine qui

    Preferisco un approccio di chiusura.

    Io uso ancora di new . Non uso Object.create . Io non uso this .

    Uso ancora il new come mi piace la natura dichiarativa di esso.

    Considera questo per ereditarietà semplice.

     window.Quad = (function() { function Quad() { const wheels = 4; const drivingWheels = 2; function getWheelCount() { return wheels; } function getDrivingWheelCount() { return drivingWheels; } return Object.freeze({ getWheelCount, getDrivingWheelCount }); } return Object.freeze(Quad); })(); window.Car4wd = (function() { function Car4wd() { const quad = new Quad(); const spareWheels = 1; const extraDrivingWheels = 2; function getSpareWheelCount() { return spareWheels; } function getDrivingWheelCount() { return quad.getDrivingWheelCount() + extraDrivingWheels; } return Object.freeze(Object.assign({}, quad, { getSpareWheelCount, getDrivingWheelCount })); } return Object.freeze(Car4wd); })(); let myQuad = new Quad(); let myCar = new Car4wd(); console.log(myQuad.getWheelCount()); // 4 console.log(myQuad.getDrivingWheelCount()); // 2 console.log(myCar.getWheelCount()); // 4 console.log(myCar.getDrivingWheelCount()); // 4 - The overridden method is called console.log(myCar.getSpareWheelCount()); // 1 

    Feedback incoraggiato.

    Sommario:

    • Object.create() è una funzione Javascript che accetta 2 argomenti e restituisce un nuovo object.
    • Il primo argomento è un object che sarà il prototipo dell’object appena creato
    • Il secondo argomento è un object che sarà le proprietà dell’object appena creato

    Esempio:

     const proto = { talk : () => console.log('hi') } const props = { age: { writable: true, configurable: true, value: 26 } } let Person = Object.create(proto, props) console.log(Person.age); Person.talk();