391 Stimmen

Verwendung von "Object.create" anstelle von "new"

Javascript 1.9.3 / ECMAScript 5 wird eingeführt Object.create die unter anderem von Douglas Crockford durchgeführt wurde. Befürwortung für eine lange Zeit. Wie kann ich ersetzen new im folgenden Code mit 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();

(Angenommen MY_GLOBAL.nextId existiert).

Das Beste, was mir einfällt, ist:

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();

Es scheint keinen Vorteil zu geben, also denke ich, dass ich es nicht verstehe. Wahrscheinlich bin ich zu neoklassisch. Wie sollte ich Object.create um den Benutzer 'bob' zu erstellen?

267voto

Christian C. Salvadó Punkte 763569

Bei nur einer Vererbungsstufe können Sie in Ihrem Beispiel möglicherweise nicht die wirklichen Vorteile von Object.create .

Diese Methoden ermöglichen eine einfache Implementierung von differentielle Vererbung in der Objekte direkt von anderen Objekten erben können.

Auf Ihrem userB Beispiel, ich glaube nicht, dass Ihr init Methode sollte öffentlich sein oder sogar existieren, wenn Sie diese Methode auf einer bestehenden Objektinstanz erneut aufrufen, wird die id y name Eigenschaften ändern.

Object.create können Sie Objekteigenschaften mit seinem zweiten Argument initialisieren, z.B.:

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
  }
});

Wie Sie sehen können, können die Eigenschaften am zweiten Argument von Object.create mit einem Objektliteral, das eine ähnliche Syntax wie die der Object.defineProperties y Object.defineProperty Methoden.

Damit können Sie die Eigenschaftsattribute festlegen ( enumerable , writable , oder configurable ), was sehr nützlich sein kann.

56voto

Noel Abrahams Punkte 7413

Es gibt wirklich keinen Vorteil bei der Verwendung von Object.create(...) über new object .

Die Befürworter dieser Methode nennen im Allgemeinen eher zweideutige Vorteile: "Skalierbarkeit" oder " natürlicher für JavaScript " usw.

Ich habe jedoch noch kein konkretes Beispiel gesehen, das zeigt, dass Object.create hat jede Vorteile gegenüber der Verwendung von new . Im Gegenteil, es gibt bekannte Probleme mit ihr. Sam Elsamman beschreibt, was passiert, wenn es verschachtelte Objekte gibt und Object.create(...) verwendet wird :

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!!!

Dies geschieht, weil Object.create(...) befürwortet eine Praxis, bei der Daten wird verwendet, um neue Objekte zu erstellen; hier wird die Animal Datum wird Teil des Prototyps von lion y bird und verursacht Probleme, wenn es geteilt wird. Bei der Verwendung von new ist die prototypische Vererbung explizit:

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

Was die optionalen Eigenschaftsattribute betrifft, die in Object.create(...) können diese mit Object.defineProperties(...) .

42voto

John Punkte 445

Object.create ist in einigen Browsern noch nicht Standard, z.B. IE8, Opera v11.5, Konq 4.3 haben es nicht. Sie können Douglas Crockfords Version von Object.create für diese Browser verwenden, aber diese enthält nicht den zweiten Parameter "Initialisierungsobjekt", der in der Antwort von CMS verwendet wird.

Für browserübergreifenden Code ist eine Möglichkeit, die Objektinitialisierung in der Zwischenzeit zu erhalten, die Anpassung von Crockfords Object.create. Hier ist eine Methode:-

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()
}

Dadurch wird die prototypische Vererbung nach Crockford beibehalten, und es wird auch geprüft, ob eine init-Methode im Objekt vorhanden ist, die dann mit Ihren Parametern ausgeführt wird, z. B. new man('John','Smith'). Ihr Code wird dann:-

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();

So erbt bob die sayHello-Methode und hat nun eigene Eigenschaften id=1 und name='Bob'. Diese Eigenschaften sind natürlich sowohl beschreibbar als auch aufzählbar. Dies ist auch eine viel einfachere Art der Initialisierung als ECMA Object.create, vor allem, wenn Sie sich keine Gedanken über die Attribute writable, enumerable und configurable machen.

Für die Initialisierung ohne init-Methode könnte die folgende Crockford-Mod verwendet werden:-

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()
}

Dies füllt die eigenen Eigenschaften von userB in der Reihenfolge, in der sie definiert sind, mit den Object.gen-Parametern von links nach rechts nach dem userB-Parameter. Es wird die for(prop in o) Schleife verwendet, so dass nach ECMA-Standards nicht garantiert werden kann, dass die Reihenfolge der Eigenschaftsaufzählung mit der Reihenfolge der Eigenschaftsdefinition übereinstimmt. Mehrere Codebeispiele, die auf (4) gängigen Browsern getestet wurden, zeigen jedoch, dass sie gleich sind, sofern der hasOwnProperty-Filter verwendet wird, und manchmal auch, wenn dies nicht der Fall ist.

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());

Etwas einfacher würde ich sagen als Object.build, da userB keine init-Methode benötigt. Außerdem ist userB kein spezieller Konstruktor, sondern sieht aus wie ein normales Singleton-Objekt. Mit dieser Methode können Sie also aus normalen einfachen Objekten konstruieren und initialisieren.

28voto

NamiW Punkte 1542

TL;DR:

new Computer() ruft die Konstruktorfunktion auf Computer(){} für eine Zeit, während Object.create(Computer.prototype) wird nicht.

Alle Vorteile beruhen auf diesem Punkt.

Nebenbemerkung zur Leistung: Konstruktoraufrufe wie new Computer() wird von der Engine stark optimiert, so dass es sogar schneller sein kann als Object.create .

14voto

samfrances Punkte 2775

Sie könnten die init Methodenrückgabe this und ketten Sie die Aufrufe wie folgt aneinander:

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');

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X