Antworten:
Der grundlegende Unterschied besteht darin, dass eine Konstruktorfunktion mit dem new
Schlüsselwort verwendet wird (wodurch JavaScript automatisch ein neues Objekt erstellt, this
innerhalb der Funktion auf dieses Objekt festgelegt und das Objekt zurückgegeben wird):
var objFromConstructor = new ConstructorFunction();
Eine Factory-Funktion wird wie eine "reguläre" Funktion aufgerufen:
var objFromFactory = factoryFunction();
Damit es jedoch als "Fabrik" betrachtet werden kann, muss eine neue Instanz eines Objekts zurückgegeben werden: Sie würden es nicht als "Fabrik" -Funktion bezeichnen, wenn es nur einen Booleschen Wert oder etwas zurückgibt. Dies geschieht nicht automatisch wie bei new
, ermöglicht jedoch in einigen Fällen mehr Flexibilität.
In einem wirklich einfachen Beispiel könnten die oben genannten Funktionen ungefähr so aussehen:
function ConstructorFunction() {
this.someProp1 = "1";
this.someProp2 = "2";
}
ConstructorFunction.prototype.someMethod = function() { /* whatever */ };
function factoryFunction() {
var obj = {
someProp1 : "1",
someProp2 : "2",
someMethod: function() { /* whatever */ }
};
// other code to manipulate obj in some way here
return obj;
}
Natürlich können Sie Factory-Funktionen viel komplizierter machen als dieses einfache Beispiel.
Ein Vorteil von Factory-Funktionen besteht darin, dass das zurückzugebende Objekt je nach Parameter von verschiedenen Typen sein kann.
someMethod
Objekte, die von der Factory zurückgegeben wurden, und dort wird es etwas neblig. Wenn dies innerhalb der Factory-Funktion nur der Fall var obj = { ... , someMethod: function() {}, ... }
ist, würde dies dazu führen, dass jedes zurückgegebene Objekt eine andere Kopie enthält, von someMethod
der wir möglicherweise nicht möchten. Hier würde die Verwendung new
und prototype
Funktion innerhalb der Fabrik helfen.
new
mit der Konstruktorfunktion zu verwenden. Ich dachte, hier muss man vielleicht sehen, wie Konstruktoren durch Factory-Funktionen ersetzt werden, und dort dachte ich, dass die Konsistenz in den Beispielen erforderlich ist. Auf jeden Fall ist die Antwort informativ genug. Dies war nur ein Punkt, den ich ansprechen wollte, nicht dass ich die Qualität der Antwort in irgendeiner Weise herabsetze.
new
intern oder Object.create()
zum Erstellen eines Objekts mit einem bestimmten Prototyp verwendet werden.
In den meisten Büchern lernen Sie, Konstruktoren und zu verwenden new
this
bezieht sich auf das neue Objekt
Manche Leute mögen die Art zu var myFoo = new Foo();
lesen.
Details der Instanziierung werden (über die new
Anforderung) in die aufrufende API übertragen , sodass alle Aufrufer eng mit der Konstruktorimplementierung verbunden sind. Wenn Sie jemals die zusätzliche Flexibilität der Fabrik benötigen, müssen Sie alle Anrufer umgestalten (zugegebenermaßen eher der Ausnahmefall als die Regel).
Das Vergessen new
ist ein so häufiger Fehler, dass Sie unbedingt eine Boilerplate-Prüfung hinzufügen sollten, um sicherzustellen, dass der Konstruktor korrekt aufgerufen wird ( if (!(this instanceof Foo)) { return new Foo() }
). BEARBEITEN: Seit ES6 (ES2015) können Sie new
mit einem class
Konstruktor nicht vergessen , oder der Konstruktor wird einen Fehler auslösen .
Wenn Sie die instanceof
Prüfung durchführen, bleibt Unklarheit darüber, ob dies new
erforderlich ist oder nicht . Meiner Meinung nach sollte es nicht sein. Sie haben die new
Anforderung effektiv kurzgeschlossen , was bedeutet, dass Sie den Nachteil Nr. 1 beseitigen können. Aber dann haben Sie nur eine Factory-Funktion außer dem Namen , mit zusätzlichem Boilerplate, einem Großbuchstaben und einem weniger flexiblen this
Kontext.
Mein Hauptanliegen ist jedoch, dass es gegen das Open / Closed-Prinzip verstößt. Sie beginnen mit dem Exportieren eines Konstruktors, Benutzer verwenden den Konstruktor und stellen dann fest, dass Sie stattdessen die Flexibilität einer Factory benötigen (z. B. um die Implementierung auf die Verwendung von Objektpools umzustellen oder über Ausführungskontexte hinweg zu instanziieren oder um haben mehr Vererbungsflexibilität mit prototypischem OO).
Du steckst aber fest. Sie können die Änderung nicht vornehmen, ohne den gesamten Code zu beschädigen, mit dem Ihr Konstruktor aufgerufen wird new
. Sie können beispielsweise nicht zur Verwendung von Objektpools wechseln, um die Leistung zu steigern.
Die Verwendung von Konstruktoren führt zu einer Täuschung instanceof
, die nicht über Ausführungskontexte hinweg funktioniert und nicht funktioniert, wenn Ihr Konstruktorprototyp ausgetauscht wird. Dies schlägt auch fehl, wenn Sie zunächst this
von Ihrem Konstruktor zurückkehren und dann zum Exportieren eines beliebigen Objekts wechseln, was Sie tun müssen, um das fabrikähnliche Verhalten in Ihrem Konstruktor zu aktivieren.
Weniger Code - kein Boilerplate erforderlich.
Sie können ein beliebiges Objekt zurückgeben und einen beliebigen Prototyp verwenden. Dies gibt Ihnen mehr Flexibilität beim Erstellen verschiedener Objekttypen, die dieselbe API implementieren. Zum Beispiel ein Media Player, der Instanzen von HTML5 und Flash Playern erstellen kann, oder eine Ereignisbibliothek, die DOM-Ereignisse oder Web Socket-Ereignisse ausgeben kann. Fabriken können Objekte auch über Ausführungskontexte hinweg instanziieren, Objektpools nutzen und flexiblere prototypische Vererbungsmodelle ermöglichen.
Sie müssten nie von einer Fabrik zu einem Konstruktor konvertieren, daher wird Refactoring niemals ein Problem sein.
Keine Unklarheit über die Verwendung new
. Tu es nicht. (Es wird this
sich schlecht benehmen, siehe nächster Punkt).
this
verhält sich wie gewohnt - Sie können es also verwenden, um auf das übergeordnete Objekt zuzugreifen (z. B. innerhalb player.create()
, this
bezieht sich player
wie bei jedem anderen Methodenaufruf auf. call
und apply
auch this
wie erwartet neu zuweisen. Wenn Sie Prototypen auf dem übergeordneten Objekt speichern, wird dies verwendet kann eine großartige Möglichkeit sein, Funktionen dynamisch auszutauschen und einen sehr flexiblen Polymorphismus für Ihre Objektinstanziierung zu ermöglichen.
Keine Unklarheit darüber, ob Kapital eingesetzt werden soll oder nicht. Tu es nicht. Fusselwerkzeuge werden sich beschweren, und dann werden Sie versucht sein, zu versuchen, zu verwenden new
, und dann werden Sie den oben beschriebenen Vorteil rückgängig machen.
Manche Leute mögen den Weg var myFoo = foo();
oder var myFoo = foo.create();
lesen.
new
verhält sich nicht wie erwartet (siehe oben). Lösung: Verwenden Sie es nicht.
this
verweist nicht auf das neue Objekt (stattdessen, wenn der Konstruktor mit Punktnotation oder eckiger Klammer aufgerufen wird, z. B. foo.bar () - this
verweist foo
- wie bei jeder anderen JavaScript-Methode - auf Vorteile).
new
das Open / Closed-Prinzip verletzt. Unter medium.com/javascript-scene/… finden Sie eine viel ausführlichere Diskussion, als diese Kommentare zulassen.
new
Schlüsselwort tun, glaube ich nicht, dass das new
Schlüsselwort tatsächlich eine zusätzliche Lesbarkeit bietet. IMO, es scheint albern, durch Reifen zu springen, damit Anrufer mehr tippen können.
Ein Konstruktor gibt eine Instanz der Klasse zurück, für die Sie sie aufrufen. Eine Factory-Funktion kann alles zurückgeben. Sie würden eine Factory-Funktion verwenden, wenn Sie beliebige Werte zurückgeben müssen oder wenn eine Klasse einen großen Einrichtungsprozess hat.
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("Jack");
new
Erstellt ein Objekt, für das ein Prototyp erstellt wurde, User.prototype
und ruft User
das erstellte Objekt als this
Wert auf.
new
behandelt einen Argumentausdruck für seinen Operanden als optional:
let user = new User;
würde veranlassen new
, User
ohne Argumente aufzurufen .
new
Gibt das erstellte Objekt zurück, es sei denn, der Konstruktor gibt einen Objektwert zurück , der stattdessen zurückgegeben wird. Dies ist ein Randfall, der größtenteils ignoriert werden kann.
Von Konstruktorfunktionen erstellte Objekte erben Eigenschaften von der Konstruktoreigenschaft prototype
und geben mit dem instanceOf
Operator für die Konstruktorfunktion true zurück .
Die oben genannten Verhaltensweisen können fehlschlagen, wenn Sie den Wert der Konstruktoreigenschaft dynamisch ändern, prototype
nachdem Sie den Konstruktor bereits verwendet haben. Dies ist selten und kann nicht geändert werden, wenn der Konstruktor mit dem class
Schlüsselwort erstellt wurde.
Konstruktorfunktionen können mit dem extends
Schlüsselwort erweitert werden .
Konstruktorfunktionen können nicht null
als Fehlerwert zurückgegeben werden. Da es sich nicht um einen Objektdatentyp handelt, wird er von ignoriert new
.
function User(name, age) {
return {
name,
age,
}
};
let user = User("Tom", 23);
Hier wird die Factory-Funktion ohne aufgerufen new
. Die Funktion ist vollständig für die direkte oder indirekte Verwendung der Argumente und des zurückgegebenen Objekttyps verantwortlich. In diesem Beispiel wird ein einfaches [Objektobjekt] mit einigen Eigenschaften zurückgegeben, die aus Argumenten festgelegt wurden.
Versteckt die Komplexität der Implementierung der Objekterstellung auf einfache Weise vor dem Aufrufer. Dies ist besonders nützlich für native Codefunktionen in einem Browser.
Die Factory-Funktion muss nicht immer Objekte des gleichen Typs zurückgeben und kann sogar null
als Fehleranzeige zurückgegeben werden.
In einfachen Fällen können Fabrikfunktionen in Struktur und Bedeutung einfach sein.
Zurückgegebene Objekte erben im Allgemeinen nicht von der prototype
Eigenschaft der Factory-Funktion und kehren false
von zurück instanceOf factoryFunction
.
Die Factory-Funktion kann mit dem extends
Schlüsselwort nicht sicher erweitert werden, da erweiterte Objekte von der prototype
Eigenschaft factory-Funktionen anstelle der prototype
Eigenschaft des von der Factory-Funktion verwendeten Konstruktors erben würden .
Fabriken sind "immer" besser. Bei Verwendung objektorientierter Sprachen dann
Die Implementierungen (die mit new erstellten tatsächlichen Objekte) sind dem werkseitigen Benutzer / Verbraucher nicht zugänglich. Dies bedeutet, dass der Factory-Entwickler erweitern und neue Implementierungen erstellen kann, solange er nicht gegen den Vertrag verstößt ... und der Factory-Consumer nur von der neuen API profitieren kann, ohne seinen Code ändern zu müssen ... Wenn sie eine neue und eine "neue" Implementierung verwendet haben, müssen sie jede Zeile ändern, die "neu" verwendet, um die "neue" Implementierung zu verwenden ... mit der Fabrik ändert sich ihr Code nicht ...
Fabriken - besser als alles andere - das Federgerüst basiert vollständig auf dieser Idee.
Fabriken sind eine Abstraktionsebene und wie alle Abstraktionen kostenintensiv. Wenn Sie auf eine fabrikbasierte API stoßen, kann es für den API-Konsumenten eine Herausforderung sein, herauszufinden, was die Fabrik für eine bestimmte API ist. Bei Konstruktoren ist die Auffindbarkeit trivial.
Bei der Entscheidung zwischen Kunden und Fabriken müssen Sie entscheiden, ob die Komplexität durch den Nutzen gerechtfertigt ist.
Es ist erwähnenswert, dass Javascript-Konstruktoren beliebige Fabriken sein können, indem sie etwas anderes als dieses oder undefinierte zurückgeben. In js können Sie also das Beste aus beiden Welten herausholen - erkennbare API und Objektpooling / Caching.
new
, Ändern des Verhaltens von this
, Ändern des Rückgabewerts, Verbinden eines Prototyps ref, Aktivieren instanceof
(was für diesen Zweck liegt und nicht verwendet werden sollte). Angeblich sind all dies "Merkmale". In der Praxis beeinträchtigen sie Ihre Codequalität.
Für die Unterschiede hat Eric Elliott sehr gut geklärt,
Aber für die zweite Frage:
Wann sollte man eins anstelle des anderen verwenden?
Wenn Sie aus dem objektorientierten Hintergrund kommen, sieht die Konstruktorfunktion für Sie natürlicher aus. Auf diese Weise sollten Sie nicht vergessen, das new
Schlüsselwort zu verwenden .