Wie heißt dieses JavaScript-Muster und warum wird es verwendet?

100

Ich studiere THREE.js und habe ein Muster festgestellt, in dem Funktionen wie folgt definiert sind:

var foo = ( function () {
    var bar = new Bar();

    return function ( ) {
        //actual logic using bar from above.
        //return result;
    };
}());

(Beispiel siehe Raycast-Methode hier ).

Die normale Variante einer solchen Methode würde folgendermaßen aussehen:

var foo = function () {
    var bar = new Bar();

    //actual logic.
    //return result;
};

Vergleicht man die erste Version mit der normalen Variante, so scheint sich die erste darin zu unterscheiden:

  1. Es weist das Ergebnis einer selbstausführenden Funktion zu.
  2. Es definiert eine lokale Variable innerhalb dieser Funktion.
  3. Es gibt die eigentliche Funktion zurück, die die Logik enthält, die die lokale Variable verwendet.

Der Hauptunterschied besteht also darin, dass in der ersten Variante der Balken bei der Initialisierung nur einmal zugewiesen wird, während die zweite Variante diese temporäre Variable bei jedem Aufruf erstellt.

Meine beste Vermutung, warum dies verwendet wird, ist, dass es die Anzahl der Instanzen für Balken begrenzt (es wird nur eine geben) und somit Speicherverwaltungsaufwand spart.

Meine Fragen:

  1. Ist diese Annahme richtig?
  2. Gibt es einen Namen für dieses Muster?
  3. Warum wird das verwendet?
Patrick Klug
quelle
1
@ ChrisHayes fair genug. Ich habe es als THREE.js markiert, weil ich dachte, dass THREE.js Mitwirkende am besten qualifiziert sind, um dies zu beantworten, aber ja, es ist eine generische JS-Frage.
Patrick Klug
2
Ich glaube, es heißt Verschlüsse. Sie können darüber lesen.
StackFlowed
1
Wenn dies der einzige Ort ist, an dem ein Balken instanziiert wird, handelt es sich um ein Singleton- Muster.
Paul
8
Nicht unbedingt, um Speicherplatz zu sparen, aber es kann den Status über Aufrufe hinweg beibehalten
Juan Mendes
2
@wrongAnswer: nicht genau. hier wird die anonyme Funktion (die der Abschluss wäre) sofort ausgeführt.
NJZK2

Antworten:

100

Ihre Annahmen sind fast richtig. Lassen Sie uns diese zuerst überprüfen.

  1. Es weist die Rückgabe einer selbstausführenden Funktion zu

Dies wird als sofort aufgerufener Funktionsausdruck oder IIFE bezeichnet

  1. Es definiert eine lokale Variable innerhalb dieser Funktion

Auf diese Weise können private Objektfelder in JavaScript verwendet werden, da das privateSchlüsselwort oder die Funktionalität nicht anderweitig bereitgestellt werden.

  1. Es gibt die eigentliche Funktion zurück, die die Logik enthält, die die lokale Variable verwendet.

Auch hier ist der Hauptpunkt, dass diese lokale Variable privat ist .

Gibt es einen Namen für dieses Muster?

AFAIK können Sie dieses Muster nennen Modul Muster . Zitat:

Das Modulmuster kapselt "Datenschutz", Status und Organisation mithilfe von Schließungen. Es bietet eine Möglichkeit, eine Mischung aus öffentlichen und privaten Methoden und Variablen zu verpacken, Teile vor dem Eindringen in den globalen Bereich zu schützen und versehentlich mit der Benutzeroberfläche eines anderen Entwicklers zusammenzustoßen. Mit diesem Muster wird nur eine öffentliche API zurückgegeben, wodurch alles andere innerhalb des Abschlusses privat bleibt.

Wenn ich diese beiden Beispiele vergleiche, sind meine besten Vermutungen darüber, warum das erste verwendet wird, folgende:

  1. Es implementiert das Singleton-Entwurfsmuster.
  2. Anhand des ersten Beispiels kann gesteuert werden, wie ein Objekt eines bestimmten Typs erstellt werden kann. Eine enge Übereinstimmung mit diesem Punkt können statische Factory-Methoden sein, wie in Effective Java beschrieben.
  3. Es ist effizient, wenn Sie jedes Mal denselben Objektstatus benötigen.

Wenn Sie jedoch jedes Mal nur das Vanilleobjekt benötigen, wird dieses Muster wahrscheinlich keinen Wert hinzufügen.

MD. Sahib Bin Mahboob
quelle
1
+1 für die korrekte Identifizierung des Musters aus Addy Osmanis Buch. Sie haben Recht mit Ihrer Benennung - dies ist in der Tat das Modulmuster - das aufschlussreiche Modulmuster darin übrigens.
Benjamin Gruenbaum
4
Ich stimme Ihrer Antwort zu, mit Ausnahme des Teils "Keine sofort einsatzbereiten privaten Variablen". Alle JS-Variablen sind lexikalisch "out-of-the-box", was ein leistungsfähigerer / allgemeiner Mechanismus ist als "private" Variablen (z. B. wie in Java zu finden). Daher unterstützt JS "private" Variablen als Sonderfall für den Umgang mit allen Variablen.
Warbo
Ich denke mit "privaten Variablen" meinten Sie privates "Objektfeld"
Ian Ringrose
1
Nur für die Details: klauskomenda.com/code/javascript-programming-patterns/#module
Nagaraj Tantri
4
@LukaHorvat: Tatsächlich ist Javascript nicht "mächtiger" als andere Sprachen (ich bevorzuge den Ausdruck "expressiv"). Tatsächlich ist es weniger aussagekräftig, da die einzige Möglichkeit, Ihre Variablen zu schützen, darin besteht, sie in Funktionen einzuschließen, um Unsinn bei der Wiederverwendung von Variablen zu vermeiden. Das Modulmuster ist eine unabdingbare Voraussetzung, um guten Javascript-Code zu erstellen, aber es ist kein Merkmal der Sprache, sondern eher eine traurige Problemumgehung, um nicht von den Schwächen der Sprache gebissen zu werden.
Falanwe
11

Es begrenzt die Objektinitialisierungskosten und stellt zusätzlich sicher, dass alle Funktionsaufrufe dasselbe Objekt verwenden. Auf diese Weise kann beispielsweise der Status im Objekt gespeichert werden, damit zukünftige Aufrufe verwendet werden können.

Während es möglich ist, dass es die Speichernutzung einschränkt, sammelt der GC normalerweise sowieso nicht verwendete Objekte, so dass dieses Muster wahrscheinlich nicht viel hilft.

Dieses Muster ist eine spezielle Form des Verschlusses .

Fengyang Wang
quelle
1
In JS wird es normalerweise als "Modul" bezeichnet
Lesha Ogonkov
2
Ich würde es nicht per se "eine bestimmte Form der Schließung" nennen. Es ist ein Muster, das einen Verschluss verwendet. Der Name des Musters ist noch zu vergeben.
Chris Hayes
4
Braucht es wirklich einen Namen? Muss alles ein Muster sein? Brauchen wir wirklich eine endlose Taxonomie von "Modulmuster" -Varianten? Kann es nicht einfach "ein IIFE mit einigen lokalen Variablen sein, das eine Funktion zurückgibt?"
Dagg Nabbit
3
@DaggNabbit Wenn die Frage lautet "Wie heißt dieses Muster"? Ja, es braucht einen Namen oder ein überzeugendes Argument, dass es keinen hat. Außerdem existieren Muster aus einem bestimmten Grund. Ich verstehe nicht, warum Sie hier gegen sie schimpfen.
Chris Hayes
4
@ChrisHayes Wenn es einen Namen braucht, warum sollten Sie ein Argument vorbringen, dass es keinen hat? Das macht keinen Sinn. Wenn es einen braucht, darf es keinen haben. Ich habe kein Problem mit Mustern, aber ich denke nicht, dass es notwendig ist, jedes einzelne einfache Idiom als Muster zu klassifizieren. Dies führt zu begrenztem Denken ("Ist dies ein Modulmuster? Benutze ich das Modulmuster richtig?" Vs. "Ich habe ein IIFE mit einigen lokalen Variablen, die eine Funktion zurückgeben. Funktioniert dieses Design für mich?")
Dagg Nabbit
8

Ich bin nicht sicher, ob dieses Muster einen korrekteren Namen hat, aber dies sieht für mich wie ein Modul aus, und der Grund, warum es verwendet wird, besteht darin, den Status sowohl zu kapseln als auch beizubehalten.

Der Abschluss (identifiziert durch eine Funktion innerhalb einer Funktion) stellt sicher, dass die innere Funktion Zugriff auf die Variablen innerhalb der äußeren Funktion hat.

In dem von Ihnen angegebenen Beispiel wird die innere Funktion foodurch Ausführen der äußeren Funktion zurückgegeben (und zugewiesen ), was bedeutet, dass sie tmpObjectweiterhin innerhalb des Abschlusses lebt und mehrere Aufrufe der inneren Funktion foo()für dieselbe Instanz von ausgeführt werden tmpObject.

itsmejodie
quelle
5

Der Hauptunterschied zwischen Ihrem Code und dem Three.js-Code besteht darin, dass die Variable im Three.js-Code tmpObjectnur einmal initialisiert und dann von jedem Aufruf der zurückgegebenen Funktion gemeinsam genutzt wird.

Dies wäre nützlich, um einen bestimmten Status zwischen Aufrufen beizubehalten, ähnlich wie staticVariablen in C-ähnlichen Sprachen verwendet werden.

tmpObject ist eine private Variable, die nur für die innere Funktion sichtbar ist.

Es ändert die Speichernutzung, ist jedoch nicht zum Speichern von Speicher ausgelegt.

mcfedr
quelle
5

Ich möchte zu diesem interessanten Thread beitragen, indem ich mich auf das Konzept des aufschlussreichen Modulmusters ausdehne, das sicherstellt, dass alle Methoden und Variablen privat bleiben, bis sie explizit verfügbar gemacht werden.

Geben Sie hier die Bildbeschreibung ein

Im letzteren Fall würde die Additionsmethode als Calculator.add () aufgerufen;

Carlodurso
quelle
0

In dem bereitgestellten Beispiel verwendet das erste Snippet für jeden Aufruf der Funktion foo () dieselbe Instanz von tmpObject, wobei tmpObject wie im zweiten Snippet jedes Mal eine neue Instanz ist.

Ein Grund, warum das erste Snippet möglicherweise verwendet wurde, besteht darin, dass die Variable tmpObject von Aufrufen von foo () gemeinsam genutzt werden kann, ohne dass ihr Wert in den Bereich gelangt, in dem foo () deklariert ist.

Die nicht sofort ausgeführte Funktionsversion des ersten Snippets würde tatsächlich so aussehen:

var tmpObject = new Bar();

function foo(){
    // Use tmpObject.
}

Beachten Sie jedoch, dass diese Version tmpObject im selben Bereich wie foo () hat, sodass es später bearbeitet werden kann.

Ein besserer Weg, um die gleiche Funktionalität zu erreichen, wäre die Verwendung eines separaten Moduls:

Modul 'foo.js':

var tmpObject = new Bar();

module.exports = function foo(){
    // Use tmpObject.
};

Modul 2:

var foo = require('./foo');

Ein Vergleich zwischen der Leistung eines IEF und einer benannten foo-Erstellerfunktion: http://jsperf.com/ief-vs-named-function

Kory Nunn
quelle
3
Ihr "besseres" Beispiel funktioniert nur in NodeJS und Sie haben nicht erklärt, wie es besser ist.
Benjamin Gruenbaum
Ein separates Modul zu erstellen ist nicht "besser", es ist nur anders. Dies ist insbesondere eine Möglichkeit, Funktionen höherer Ordnung auf Objekte erster Ordnung zu reduzieren. Code erster Ordnung ist in der Regel einfacher zu durchlaufen, ist jedoch im Allgemeinen ausführlicher und zwingt uns, Zwischenergebnisse zu überprüfen.
Warbo
@BenjaminGruenbaum Module sind nicht nur in Node, es gibt viele clientseitige Modullösungen, zum Beispiel browserify. Ich halte die Modullösung für "besser", da sie besser lesbar und leichter zu debuggen ist und expliziter darüber ist, was wo in Umfang ist.
Kory Nunn