Hat diese Art der Definition von JS-Objekten einen Zweck?

87

Ich verwalte alten Code und habe festgestellt, dass das folgende Muster zum Definieren von Objekten verwendet wird:

var MyObject = {};

(function (root) {

    root.myFunction = function (foo) {
        //do something
    };

})(MyObject);

Hat das einen Zweck? Ist es gleichbedeutend damit, nur Folgendes zu tun?

var MyObject = {

    myFunction : function (foo) {
        //do something
    };

};

Ich bin nicht im Begriff, mich auf eine heilige Suche zu begeben, um die gesamte Codebasis nach meinen Wünschen umzugestalten, aber ich möchte wirklich den Grund für diese Art der Definition von Objekten verstehen.

Vielen Dank!

Sebastián Vansteenkiste
quelle
1
In Ihrem genauen Beispiel gibt es keinen Unterschied. Wenn Sie es erweitern, kann es einen Unterschied geben, aber es werden auch verschiedene Ansätze zum Einsatz kommen.
Travis J
Es macht keinen Unterschied, Objekte werden sozusagen als Kopie einer Referenz übergeben. Selbst wenn Sie die myFunction innerhalb des IIFE definieren, ist sie auch außerhalb des IIFE zugänglich.
Adeneo
1
@adeneo Nicht für dieses Beispiel, indem myFunctioneinige außerhalb von sich selbst definierte Variablen verwendet werden könnten , auf die von außen nicht zugegriffen werden kann . Siehe meine Antwort
Juan Mendes
2
Mögliches Duplikat von Wie heißt dieses JavaScript-Muster und warum wird es verwendet? (nicht sicher, ob ich schließen soll). Siehe auch JavaScript-Namespace-Deklaration oder diese .
Bergi

Antworten:

116

Es heißt das Modulmuster http://toddmotto.com/mastering-the-module-pattern/

Der Hauptgrund ist, dass Sie wirklich private Methoden und Variablen erstellen. In Ihrem Fall ist dies nicht aussagekräftig, da keine Implementierungsdetails ausgeblendet werden.

Hier ist ein Beispiel, in dem es sinnvoll ist, das Modulmuster zu verwenden.

var MyNameSpace = {};

(function(ns){
    // The value variable is hidden from the outside world
    var value = 0;

    // So is this function
    function adder(num) {
       return num + 1;
    }

    ns.getNext = function () {
       return value = adder(value);
    }
})(MyNameSpace);

var id = MyNameSpace.getNext(); // 1
var otherId = MyNameSpace.getNext(); // 2
var otherId = MyNameSpace.getNext(); // 3

Wenn Sie nur ein gerades Objekt verwenden adderund valueöffentlich werden würden

var MyNameSpace = {
    value: 0,
    adder: function(num) {
       return num + 1;
    },
    getNext: function() {
       return this.value = this.adder(this.value);
    }
}

Und Sie könnten es brechen, indem Sie Dinge wie tun

MyNameSpace.getNext(); // 1
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 1 again
delete MyNameSpace.adder;
MyNameSpace.getNext(); // error undefined is not a function

Aber mit der Modulversion

MyNameSpace.getNext(); // 1
 // Is not affecting the internal value, it's creating a new property
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 2, yessss
// Is not deleting anything
delete MyNameSpace.adder;
MyNameSpace.getNext(); // no problemo, outputs 3
Juan Mendes
quelle
2
Dies beantwortet nicht wirklich die Frage, was der Unterschied zwischen den beiden Alternativen ist.
20
@torazaburo Das Beispiel des OP war kein gutes Beispiel. Ich habe ein echtes Beispiel bereitgestellt, das zeigt, wann das Modulmuster verwendet werden soll.
Juan Mendes
Dies ns.getNext: function () {wird nicht kompiliert.
Punund
Ich hätte, wenn ich sicher wäre, wie ich es reparieren könnte. Ich dachte, es gäbe ein Konstrukt, das verhindert werden könnte delete MyNameSpace.getNext.
Punund
2
@ Punund JS hat keinen Compiler, es hat einen Interpreter:)
Frogatto
22

Der Zweck besteht darin, die Zugänglichkeit von Funktionen innerhalb des Abschlusses zu beschränken, um zu verhindern, dass andere Skripte Code darauf ausführen. Durch Wickeln um eine Schließung Sie Neudefinition der Umfang der Ausführung für alle Code innerhalb des Verschlusses und effektiv einen privaten Rahmen zu schaffen. Weitere Informationen finden Sie in diesem Artikel:

http://lupomontero.com/using-javascript-closures-to-create-private-scopes/

Aus dem Artikel:

Eines der bekanntesten Probleme in JavaScript ist die Abhängigkeit von einem globalen Bereich. Dies bedeutet im Grunde, dass alle Variablen, die Sie außerhalb einer Funktion deklarieren, im selben Namensraum leben: dem ominösen Fensterobjekt. Aufgrund der Art der Webseiten können (und werden) viele Skripte aus verschiedenen Quellen auf derselben Seite ausgeführt werden, die einen gemeinsamen globalen Bereich haben, und dies kann eine wirklich sehr schlechte Sache sein, da dies zu Namenskollisionen führen kann (Variablen mit demselben Namen) überschrieben werden) und Sicherheitsprobleme. Um das Problem zu minimieren, können wir die leistungsstarken Abschlüsse von JavaScript verwenden, um private Bereiche zu erstellen, in denen wir sicher sein können, dass unsere Variablen für andere Skripts auf der Seite unsichtbar sind.



Code:

var MyObject = {};

(function (root) {
    function myPrivateFunction() {
       return "I can only be called from within the closure";
    }

    root.myFunction = function (foo) {
        //do something
    };    

    myPrivateFunction(); // returns "I can only be called from within the closure"

})(MyObject);


myPrivateFunction(); // throws error - undefined is not a function
Jonathan Crowe
quelle
1
myFunctionist in der zweiten Version nicht im globalen Bereich. Der Zweck besteht tatsächlich darin, einen Bereich bereitzustellen, in dem interne Hilfsfunktionen definiert werden können.
Barmar
myFunctionbefindet sich im globalen Bereich, da es im globalen Objekt definiert ist myObject. In der zweiten Version kann jeder andere Code in der Anwendung ausgeführt werden myFunction. In der ersten Version hat nur Code innerhalb der Schließung Zugriff aufmyFunction
Jonathan Crowe
Nein, myFunctionkann nur MyObject.myFunction()wie die erste Version ausgeführt werden.
Barmar
@JonathanCrowe Das Beispiel des OP ist kein gutes Beispiel, es macht alles innerhalb des Moduls sichtbar, also ja, es wird außerhalb zugänglich. Siehe meine Antwort für einen nützlichen Modulfall
Juan Mendes
@ JuanMendes guter Punkt, OPs Beispiel ist keine gute Verwendung des Modulmusters
Jonathan Crowe
6

Vorteile:

  1. verwaltet Variablen im privaten Bereich.

  2. Sie können die Funktionalität des vorhandenen Objekts erweitern.

  3. Leistung wird erhöht.

Ich denke, die obigen drei einfachen Punkte sind gerade genug, um diesen Regeln zu folgen. Und um es einfach zu halten, ist es nichts anderes als das Schreiben innerer Funktionen.

Mateen
quelle
6

In dem von Ihnen gezeigten speziellen Fall gibt es keinen bedeutenden Unterschied in Bezug auf Funktionalität oder Sichtbarkeit.

Es ist wahrscheinlich, dass der ursprüngliche Codierer diesen Ansatz als eine Art Vorlage verwendet hat, mit der er private Variablen definieren kann, die bei der Definition von Dingen wie myFunction:

var MyObject = {};
(function(root) {
    var seconds_per_day = 24 * 60 * 60;   // <-- private variable
    root.myFunction = function(foo) {
        return seconds_per_day;
    };
})(MyObject);

Dies vermeidet die Berechnung bei seconds_per_dayjedem Aufruf der Funktion und verhindert gleichzeitig, dass der globale Bereich verschmutzt wird.

Es gibt jedoch nichts wesentlich anderes als nur zu sagen

var MyObject = function() {
    var seconds_per_day = 24 * 60 * 60;
    return {
        myFunction: function(foo) {
            return seconds_per_day;
        }
    };
}();

Der ursprüngliche Codierer hat es möglicherweise vorgezogen, Funktionen zum Objekt mit der deklarativen Syntax von root.myFunction = functionund nicht mit der Objekt- / Eigenschaftssyntax von hinzufügen zu können myFunction: function. Dieser Unterschied ist jedoch hauptsächlich eine Frage der Präferenz.

Die vom ursprünglichen Codierer verwendete Struktur hat jedoch den Vorteil, dass Eigenschaften / Methoden leicht an anderer Stelle im Code hinzugefügt werden können:

var MyObject = {};
(function(root) {
    var seconds_per_day = 24 * 60 * 60;
    root.myFunction = function(foo) {
        return seconds_per_day;
    };
})(MyObject);

(function(root) {
    var another_private_variable = Math.pi;
    root.myFunction2 = function(bar) { };
})(MyObject);

Unterm Strich müssen Sie diesen Ansatz nicht anwenden, wenn Sie ihn nicht benötigen, aber Sie müssen ihn auch nicht ändern, da er einwandfrei funktioniert und tatsächlich einige Vorteile bietet.


quelle
6
  1. Das erste Muster kann als Modul verwendet werden, das ein Objekt nimmt und dieses Objekt mit einigen Änderungen zurückgibt. Mit anderen Worten, Sie können solche Module wie folgt definieren.

    var module = function (root) {
        root.myFunction = function (foo) {
            //do something
        };
    }

    Und benutze es wie:

    var obj = {};
    module(obj);

    Ein Vorteil könnte also die Wiederverwendbarkeit dieses Moduls für spätere Verwendungen sein.


  1. Im ersten Muster können Sie einen privaten Bereich definieren, in dem Ihre privaten Daten wie private Eigenschaften und Methoden gespeichert werden. Betrachten Sie zum Beispiel dieses Snippet:

    (function (root) {
    
        // A private property
        var factor = 3;
    
        root.multiply = function (foo) {
            return foo * factor;
        };
    })(MyObject);

  1. Dieses Muster kann verwendet werden, um allen Objekttypen wie Arrays, Objektliteralen und Funktionen eine Methode oder Eigenschaft hinzuzufügen.

    function sum(a, b) {
        return a + b;
    }
    
    (function (root) {
        // A private property
        var factor = 3;
        root.multiply = function (foo) {
            return foo * factor;
        };
    })(sum);
    
    console.log(sum(1, 2)); // 3
    console.log(sum.multiply(4)); // 12

Meiner Meinung nach könnte der Hauptvorteil der zweite sein (Schaffung eines privaten Bereichs)

Frogatto
quelle
5

Dieses Muster bietet einen Bereich, in dem Sie Hilfsfunktionen definieren können, die im globalen Bereich nicht sichtbar sind:

(function (root) {

    function doFoo() { ... };

    root.myFunction = function (foo) {
        //do something
        doFoo();
        //do something else
    };

})(MyObject);

doFoo ist lokal für die anonyme Funktion und kann von außen nicht referenziert werden.

Barmar
quelle