644 Stimmen

Welchen Sinn hat es, ganze Javascript-Dateien in anonyme Funktionen wie "(function(){ })()" zu verpacken?

Ich habe in letzter Zeit viel über Javascript gelesen und mir ist aufgefallen, dass die gesamte Datei wie folgt in die zu importierenden .js-Dateien eingepackt ist.

(function() {
    ... 
    code
    ...
})();

Was ist der Grund dafür, dies zu tun und nicht einfach eine Reihe von Konstruktorfunktionen zu verwenden?

850voto

Vivin Paliath Punkte 90791

Es ist in der Regel zu Namespace (siehe später) und steuern die Sichtbarkeit von Mitglied Funktionen und / oder Variablen. Man kann es sich wie eine Objektdefinition vorstellen. Der technische Name dafür ist ein Unmittelbar aufgerufener Funktionsausdruck (IIFE). jQuery-Plugins werden normalerweise so geschrieben.

In Javascript können Sie Funktionen verschachteln. Also ist das Folgende legal:

function outerFunction() {
   function innerFunction() {
      // code
   }
}

Jetzt können Sie anrufen outerFunction() aber die Sichtbarkeit von innerFunction() ist begrenzt auf den Umfang der outerFunction() Das bedeutet, dass es privat ist für outerFunction() . Es folgt im Grunde dem gleichen Prinzip wie Variablen in Javascript:

var globalVariable;

function someFunction() {
   var localVariable;
}

Dementsprechend:

function globalFunction() {

   var localFunction1 = function() {
       //I'm anonymous! But localFunction1 is a reference to me!
   };

   function localFunction2() {
      //I'm named!
   }
}

In dem obigen Szenario können Sie globalFunction() von überall aus, aber Sie können nicht localFunction1 o localFunction2 .

Was Sie tun, wenn Sie schreiben (function() { ... })() Das bedeutet, dass Sie den Code innerhalb der ersten Klammer zu einem Funktionsliteral machen (d. h. das gesamte "Objekt" ist eigentlich eine Funktion). Danach rufen Sie die Funktion selbst auf (die letzte () ), die Sie gerade definiert haben. Der Hauptvorteil ist also, wie bereits erwähnt, dass Sie private Methoden/Funktionen und Eigenschaften haben können:

(function() {
   var private_var;

   function private_function() {
     //code
   }
})();

Im ersten Beispiel würden Sie explizit Folgendes aufrufen globalFunction mit dem Namen, um es auszuführen. Das heißt, Sie würden einfach Folgendes tun globalFunction() um sie auszuführen. Aber im obigen Beispiel definieren Sie nicht nur eine Funktion; Sie definieren et sie in einem Zug aufzurufen. Das bedeutet, dass Ihre JavaScript-Datei sofort ausgeführt wird, wenn sie geladen wird. Das können Sie natürlich auch tun:

function globalFunction() {
    // code
}
globalFunction();

Das Verhalten wäre weitgehend dasselbe, mit Ausnahme eines bedeutenden Unterschieds: Sie vermeiden die Verschmutzung des globalen Bereichs, wenn Sie ein IIFE verwenden (als Konsequenz bedeutet dies auch, dass Sie die Funktion nicht mehrfach aufrufen können, da sie keinen Namen hat, aber da diese Funktion nur einmal ausgeführt werden soll, ist dies wirklich kein Problem).

Das Schöne an IIFEs ist, dass man auch Dinge darin definieren kann und nur die Teile, die man will, nach außen hin sichtbar macht (ein Beispiel für Namespacing, so dass man im Grunde seine eigene Bibliothek/Plugin erstellen kann):

var myPlugin = (function() {
 var private_var;

 function private_function() {
 }

 return {
    public_function1: function() {
    },
    public_function2: function() {
    }
 }
})()

Jetzt können Sie anrufen myPlugin.public_function1() aber Sie können nicht auf private_function() ! Also ziemlich ähnlich wie bei einer Klassendefinition. Um dies besser zu verstehen, empfehle ich die folgenden Links zur weiteren Lektüre:

EDIT

Ich vergaß zu erwähnen. In diesem letzten () können Sie alles, was Sie wollen, hineinlegen. Wenn Sie zum Beispiel jQuery-Plugins erstellen, übergeben Sie in jQuery o $ etwa so:

(function(jQ) { ... code ... })(jQuery) 

Was Sie hier also tun, ist eine Funktion zu definieren, die einen Parameter annimmt (genannt jQ , eine lokale Variable, und bekannt nur zu dieser Funktion). Dann rufen Sie die Funktion selbst auf und übergeben einen Parameter (auch genannt jQuery aber diese eine von der Außenwelt und ein Verweis auf das eigentliche jQuery selbst). Es besteht keine dringende Notwendigkeit, dies zu tun, aber es hat einige Vorteile:

  • Sie können einen globalen Parameter umdefinieren und ihm einen Namen geben, der im lokalen Bereich sinnvoll ist.
  • Es gibt einen leichten Leistungsvorteil, da es schneller ist, Dinge im lokalen Bereich nachzuschlagen, anstatt die Bereichskette hinauf in den globalen Bereich gehen zu müssen.
  • Die Komprimierung (Minification) ist von Vorteil.

Vorhin habe ich beschrieben, wie diese Funktionen beim Start automatisch ausgeführt werden, aber wenn sie automatisch ausgeführt werden, wer gibt dann die Argumente ein? Diese Technik setzt voraus, dass alle benötigten Parameter bereits als globale Variablen definiert sind. Wenn also jQuery nicht bereits als globale Variable definiert wäre, würde dieses Beispiel nicht funktionieren. Wie Sie sich vielleicht denken können, definiert jquery.js während seiner Initialisierung eine globale Variable "jQuery" sowie die bekanntere globale Variable "$", so dass dieser Code auch nach der Einbindung von jQuery funktioniert.

82voto

Adrien Be Punkte 18445

Kurzum

Resumen

In ihrer einfachsten Form zielt diese Technik darauf ab, den Code in eine Funktionsumfang .

Es hilft, die Chancen zu verringern:

  • Kollisionen mit anderen Anwendungen/Bibliotheken
  • Verschmutzungsgrad von übergeordnetem (höchstwahrscheinlich globalem) Umfang

Es nicht erkennen, wann das Dokument fertig ist - es handelt sich nicht um eine Art document.onload noch window.onload

Es ist allgemein bekannt als ein Immediately Invoked Function Expression (IIFE) o Self Executing Anonymous Function .

Code erklärt

var someFunction = function(){ console.log('wagwan!'); };

(function() {                   /* function scope starts here */
  console.log('start of IIFE');

  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();                           /* function scope ends */

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

Im obigen Beispiel wird jede Variable, die in der Funktion definiert ist (d. h. die mit var ) sind "privat" und NUR innerhalb des Funktionsbereichs zugänglich (wie Vivin Paliath es ausdrückt). Mit anderen Worten, diese Variablen sind außerhalb der Funktion nicht sichtbar/erreichbar. Siehe Live-Demo .

Javascript hat Funktions-Scoping. "Parameter und Variablen, die in einer Funktion definiert sind, sind außerhalb der Funktion nicht sichtbar, und eine Variable, die irgendwo innerhalb einer Funktion definiert ist, ist überall innerhalb der Funktion sichtbar." (aus "Javascript: The Good Parts").


Mehr Details

Alternativer Code

Letztendlich könnte der zuvor gepostete Code auch wie folgt ausgeführt werden:

var someFunction = function(){ console.log('wagwan!'); };

var myMainFunction = function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
};

myMainFunction();          // I CALL "myMainFunction" FUNCTION HERE
someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

Siehe Live-Demo .


Die Wurzeln

Iteration 1

Eines Tages hat sich wahrscheinlich jemand gedacht: "Es muss einen Weg geben, den Namen 'myMainFunction' zu vermeiden, denn wir wollen ja nur, dass er sofort ausgeführt wird."

Wenn man sich auf die Grundlagen besinnt, stellt man fest, dass:

  • expression etwas, das zu einem Wert ausgewertet wird, d.h. 3+11/x
  • statement : Codezeile(n), die etwas tun, ABER es tut no zu einem Wert auswerten, d.h. if(){}

Ähnlich verhält es sich mit Funktionsausdrücken, die einen Wert ergeben. Und eine Folge davon (nehme ich an?) ist, dass sie sofort aufgerufen werden können:

 var italianSayinSomething = function(){ console.log('mamamia!'); }();

Unser komplexeres Beispiel lautet also:

var someFunction = function(){ console.log('wagwan!'); };

var myMainFunction = function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
}();

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

Siehe Live-Demo .

Iteration 2

Der nächste Schritt ist der Gedanke "warum haben var myMainFunction = wenn wir es nicht einmal benutzen!".

Die Antwort ist einfach: Versuchen Sie, dies zu entfernen, z. B. wie unten:

 function(){ console.log('mamamia!'); }();

Siehe Live-Demo .

Es wird nicht funktionieren, weil "Funktionsdeklarationen sind nicht aufrufbar" .

Der Trick besteht darin, dass durch das Entfernen var myMainFunction = haben wir die Funktionsausdruck in eine Funktionsdeklaration . Weitere Einzelheiten hierzu finden Sie unter den Links in "Ressourcen".

Die nächste Frage lautet: "Warum kann ich es nicht als Funktionsausdruck mit etwas anderem als var myMainFunction = ?

Die Antwort lautet "Sie können", und es gibt tatsächlich viele Möglichkeiten, dies zu tun: Hinzufügen einer + , a ! , a - oder vielleicht ein Paar Klammern (wie es heute üblich ist), und ich glaube noch mehr. Als Beispiel:

 (function(){ console.log('mamamia!'); })(); // live demo: jsbin.com/zokuwodoco/1/edit?js,console.

ou

 +function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wuwipiyazi/1/edit?js,console

ou

 -function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wejupaheva/1/edit?js,console

Sobald wir also die entsprechende Änderung zu unserem "alternativen Code" hinzugefügt haben, erhalten wir genau denselben Code wie im Beispiel "Code erklärt"

var someFunction = function(){ console.log('wagwan!'); };

(function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

Lesen Sie mehr über Expressions vs Statements :


Entmystifizierung von Geltungsbereichen

Man könnte sich fragen: "Was passiert, wenn man die Variable innerhalb der Funktion NICHT 'richtig' definiert, d.h. stattdessen eine einfache Zuweisung vornimmt?"

(function() {
  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  myOtherFunction = function(){  /* oops, an assignment instead of a declaration */
    console.log('haha. got ya!');
  };
})();
myOtherFunction();         // reachable, hence works: see in the console
window.myOtherFunction();  // works in the browser, myOtherFunction is then in the global scope
myFunction();              // unreachable, will throw an error, see in the console

Siehe Live-Demo .

Wenn einer Variablen, die nicht in ihrem aktuellen Bereich deklariert wurde, ein Wert zugewiesen wird, wird grundsätzlich in der Bereichskette nachgeschaut, bis die Variable gefunden wird oder der globale Bereich erreicht wird (dann wird sie erstellt).

In einer Browserumgebung (im Gegensatz zu einer Serverumgebung wie Nodejs) wird der globale Bereich durch die window Objekt. Wir können also tun window.myOtherFunction() .

Mein Tipp für eine gute Praxis zu diesem Thema lautet immer verwenden var wenn Sie etwas definieren : egal ob es sich um eine Zahl, ein Objekt oder eine Funktion handelt, und selbst wenn sie sich im globalen Bereich befinden. Das macht den Code viel einfacher.

Nota:

  • Javascript macht no haben block scope (Aktualisierung: Lokale Variablen im Blockbereich hinzugefügt in ES6 .)
  • javascript hat nur function scope & global scope ( window Umfang in einer Browserumgebung)

Lesen Sie mehr über Javascript Scopes :


Ressourcen


Nächste Schritte

Sobald Sie dies erhalten IIFE Konzept, führt es zu dem module pattern was in der Regel mit Hilfe dieses IIFE-Musters geschieht. Viel Spaß :)

27voto

Gareth Punkte 123487

Javascript in einem Browser hat eigentlich nur ein paar effektive Bereiche: Funktionsbereich und globaler Bereich.

Wenn eine Variable nicht im Funktionsbereich liegt, befindet sie sich im globalen Bereich. Und globale Variablen sind im Allgemeinen schlecht, also ist dies ein Konstrukt, um die Variablen einer Bibliothek für sich zu behalten.

20voto

Joel Punkte 18917

Das nennt man einen Abschluss. Es versiegelt im Grunde den Code innerhalb der Funktion, so dass andere Bibliotheken nicht in ihn eingreifen können. Es ist vergleichbar mit dem Erstellen eines Namespaces in kompilierten Sprachen.

Beispiel. Angenommen, ich schreibe:

(function() {

    var x = 2;

    // do stuff with x

})();

Jetzt können andere Bibliotheken nicht mehr auf die Variable x die ich zur Verwendung in meiner Bibliothek erstellt habe.

8voto

kennebec Punkte 98551

Sie können Funktionsabschlüsse verwenden als Daten auch in größeren Ausdrücken, wie in dieser Methode zur Bestimmung der Browserunterstützung für einige der html5-Objekte.

   navigator.html5={
     canvas: (function(){
      var dc= document.createElement('canvas');
      if(!dc.getContext) return 0;
      var c= dc.getContext('2d');
      return typeof c.fillText== 'function'? 2: 1;
     })(),
     localStorage: (function(){
      return !!window.localStorage;
     })(),
     webworkers: (function(){
      return !!window.Worker;
     })(),
     offline: (function(){
      return !!window.applicationCache;
     })()
    }

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X