Kann jemand den Unterschied zwischen einer Konstruktorfunktion und einer Factory-Funktion in Javascript klären.
Wann sollte man eins anstelle des anderen verwenden?
quelle
Kann jemand den Unterschied zwischen einer Konstruktorfunktion und einer Factory-Funktion in Javascript klären.
Wann sollte man eins anstelle des anderen verwenden?
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 Fallvar obj = { ... , someMethod: function() {}, ... }
ist, würde dies dazu führen, dass jedes zurückgegebene Objekt eine andere Kopie enthält, vonsomeMethod
der wir möglicherweise nicht möchten. Hier würde die Verwendungnew
undprototype
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 oderObject.create()
zum Erstellen eines Objekts mit einem bestimmten Prototyp verwendet werden.Vorteile der Verwendung von Konstruktoren
In den meisten Büchern lernen Sie, Konstruktoren und zu verwenden
new
this
bezieht sich auf das neue ObjektManche Leute mögen die Art zu
var myFoo = new Foo();
lesen.Nachteile
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 Sienew
mit einemclass
Konstruktor nicht vergessen , oder der Konstruktor wird einen Fehler auslösen .Wenn Sie die
instanceof
Prüfung durchführen, bleibt Unklarheit darüber, ob diesnew
erforderlich ist oder nicht . Meiner Meinung nach sollte es nicht sein. Sie haben dienew
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 flexiblenthis
Kontext.Konstruktoren brechen das Open / Closed-Prinzip
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ächstthis
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.Vorteile der Verwendung von Fabriken
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 wirdthis
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. innerhalbplayer.create()
,this
bezieht sichplayer
wie bei jedem anderen Methodenaufruf auf.call
undapply
auchthis
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();
odervar myFoo = foo.create();
lesen.Nachteile
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
verweistfoo
- wie bei jeder anderen JavaScript-Methode - auf Vorteile).quelle
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 dasnew
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.
quelle
Ein Beispiel für eine Konstruktorfunktion
new
Erstellt ein Objekt, für das ein Prototyp erstellt wurde,User.prototype
und ruftUser
das erstellte Objekt alsthis
Wert auf.new
behandelt einen Argumentausdruck für seinen Operanden als optional: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.Vor-und Nachteile
Von Konstruktorfunktionen erstellte Objekte erben Eigenschaften von der Konstruktoreigenschaft
prototype
und geben mit deminstanceOf
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 demclass
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 ignoriertnew
.Ein Beispiel für eine Factory-Funktion
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.Vor-und Nachteile
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 kehrenfalse
von zurückinstanceOf factoryFunction
.Die Factory-Funktion kann mit dem
extends
Schlüsselwort nicht sicher erweitert werden, da erweiterte Objekte von derprototype
Eigenschaft factory-Funktionen anstelle derprototype
Eigenschaft des von der Factory-Funktion verwendeten Konstruktors erben würden .quelle
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.
quelle
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.
quelle
new
, Ändern des Verhaltens vonthis
, Ändern des Rückgabewerts, Verbinden eines Prototyps ref, Aktiviereninstanceof
(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:
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 .quelle