Antworten:
Der grundlegende Unterschied besteht darin, dass eine Konstruktorfunktion mit dem newSchlüsselwort verwendet wird (wodurch JavaScript automatisch ein neues Objekt erstellt, thisinnerhalb 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.
someMethodObjekte, 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 someMethodder wir möglicherweise nicht möchten. Hier würde die Verwendung newund prototypeFunktion innerhalb der Fabrik helfen.
newmit 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.
newintern 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 newAnforderung) 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 newist 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 newmit einem classKonstruktor nicht vergessen , oder der Konstruktor wird einen Fehler auslösen .
Wenn Sie die instanceofPrüfung durchführen, bleibt Unklarheit darüber, ob dies newerforderlich ist oder nicht . Meiner Meinung nach sollte es nicht sein. Sie haben die newAnforderung 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 thisKontext.
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 thisvon 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 thissich schlecht benehmen, siehe nächster Punkt).
thisverhält sich wie gewohnt - Sie können es also verwenden, um auf das übergeordnete Objekt zuzugreifen (z. B. innerhalb player.create(), thisbezieht sich playerwie bei jedem anderen Methodenaufruf auf. callund applyauch thiswie 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.
newverhält sich nicht wie erwartet (siehe oben). Lösung: Verwenden Sie es nicht.
thisverweist nicht auf das neue Objekt (stattdessen, wenn der Konstruktor mit Punktnotation oder eckiger Klammer aufgerufen wird, z. B. foo.bar () - thisverweist foo- wie bei jeder anderen JavaScript-Methode - auf Vorteile).
newdas Open / Closed-Prinzip verletzt. Unter medium.com/javascript-scene/… finden Sie eine viel ausführlichere Diskussion, als diese Kommentare zulassen.
newSchlüsselwort tun, glaube ich nicht, dass das newSchlü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");
newErstellt ein Objekt, für das ein Prototyp erstellt wurde, User.prototypeund ruft Userdas erstellte Objekt als thisWert auf.
new behandelt einen Argumentausdruck für seinen Operanden als optional:
let user = new User;
würde veranlassen new, Userohne Argumente aufzurufen .
newGibt 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 prototypeund geben mit dem instanceOfOperator für die Konstruktorfunktion true zurück .
Die oben genannten Verhaltensweisen können fehlschlagen, wenn Sie den Wert der Konstruktoreigenschaft dynamisch ändern, prototypenachdem Sie den Konstruktor bereits verwendet haben. Dies ist selten und kann nicht geändert werden, wenn der Konstruktor mit dem classSchlüsselwort erstellt wurde.
Konstruktorfunktionen können mit dem extendsSchlüsselwort erweitert werden .
Konstruktorfunktionen können nicht nullals 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 nullals 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 prototypeEigenschaft der Factory-Funktion und kehren falsevon zurück instanceOf factoryFunction.
Die Factory-Funktion kann mit dem extendsSchlüsselwort nicht sicher erweitert werden, da erweiterte Objekte von der prototypeEigenschaft factory-Funktionen anstelle der prototypeEigenschaft 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 newSchlüsselwort zu verwenden .