Was ist der Zweck, ganze Javascript-Dateien in anonyme Funktionen wie "(function () {…}) ()" zu verpacken?

584

Ich habe in letzter Zeit viel Javascript gelesen und festgestellt, dass die gesamte Datei wie folgt in die zu importierenden .js-Dateien eingeschlossen ist.

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

Was ist der Grund dafür und nicht ein einfacher Satz von Konstruktorfunktionen?

Andrew Kou
quelle
6
Da ich mir vorstelle, dass dies von vielen Leuten verwendet wird, vergessen Sie bitte nicht das Schließen.
dgh
5
Diese Technik heißt "IIFE", denke ich. Dies steht für sofort aufgerufenen Funktionsausdruck en.wikipedia.org/wiki/Immedially-invoked_function_expression
Adrien Be

Antworten:

786

Es dient normalerweise zum Namespace (siehe später) und zur Steuerung der Sichtbarkeit von Elementfunktionen und / oder Variablen. Stellen Sie es sich wie eine Objektdefinition vor. Der technische Name dafür ist ein sofort aufgerufener Funktionsausdruck (IIFE). jQuery-Plugins werden normalerweise so geschrieben.

In Javascript können Sie Funktionen verschachteln. Folgendes ist also legal:

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

Jetzt können Sie anrufen outerFunction(), aber die Sichtbarkeit von innerFunction()ist auf den Umfang von beschränkt outerFunction(), was bedeutet, dass es privat ist outerFunction(). Es folgt im Wesentlichen dem gleichen Prinzip wie Variablen in Javascript:

var globalVariable;

function someFunction() {
   var localVariable;
}

Entsprechend:

function globalFunction() {

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

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

Im obigen Szenario können Sie globalFunction()von überall aus anrufen , aber nicht localFunction1oder localFunction2.

Wenn Sie schreiben (function() { ... })(), machen Sie den Code in den ersten Klammern zu einem Funktionsliteral (was bedeutet, dass das gesamte "Objekt" tatsächlich eine Funktion ist). Danach rufen ()Sie die gerade definierte Funktion (die letzte ) selbst auf . Der Hauptvorteil davon ist, wie ich bereits erwähnt habe, 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 globalFunctionnach Namen aufrufen , um es auszuführen. Das heißt, Sie würden es einfach tun globalFunction(), um es auszuführen. Im obigen Beispiel definieren Sie jedoch nicht nur eine Funktion. Sie definieren und rufen es auf einmal auf. Dies bedeutet, dass Ihre JavaScript-Datei beim Laden sofort ausgeführt wird. Natürlich können Sie Folgendes tun:

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

Das Verhalten wäre bis auf einen wesentlichen Unterschied weitgehend dasselbe: Sie vermeiden es, den globalen Bereich zu verschmutzen, wenn Sie ein IIFE verwenden (infolgedessen bedeutet dies auch, dass Sie die Funktion nicht mehrmals aufrufen können, da sie keinen Namen hat, sondern seitdem Diese Funktion soll nur ausgeführt werden, wenn sie wirklich kein Problem darstellt.

Das Schöne an IIFEs ist, dass Sie auch Dinge innerhalb definieren und nur die Teile, die Sie möchten, der Außenwelt aussetzen können (ein Beispiel für Namespace, damit Sie im Grunde Ihre eigene Bibliothek / Ihr eigenes Plugin erstellen können):

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 zugreifen private_function()! Also ziemlich ähnlich einer Klassendefinition. Um dies besser zu verstehen, empfehle ich die folgenden Links zur weiteren Lektüre:

BEARBEITEN

Ich vergaß zu erwähnen. In diesem Finale ()können Sie alles übergeben, was Sie wollen. Wenn Sie beispielsweise jQuery-Plugins erstellen, übergeben Sie Folgendes jQueryoder möchten Folgendes $:

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

Sie definieren hier also eine Funktion, die einen Parameter akzeptiert (genannt jQ, eine lokale Variable, die nur dieser Funktion bekannt ist). Dann rufen Sie die Funktion selbst auf und übergeben einen Parameter (auch aufgerufen jQuery, aber dieser stammt aus der Außenwelt und verweist auf die eigentliche jQuery selbst). Es besteht keine dringende Notwendigkeit, dies zu tun, aber es gibt einige Vorteile:

  • Sie können einen globalen Parameter neu definieren 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, als die Bereichskette in den globalen Bereich zu verschieben.
  • Es gibt Vorteile für die Komprimierung (Minimierung).

Früher habe ich beschrieben, wie diese Funktionen beim Start automatisch ausgeführt werden. Wenn sie jedoch automatisch ausgeführt werden, wer übergibt die Argumente? Bei dieser Technik wird davon ausgegangen, dass alle von Ihnen benötigten Parameter bereits als globale Variablen definiert sind. Wenn jQuery nicht bereits als globale Variable definiert wäre, würde dieses Beispiel nicht funktionieren. Wie Sie vielleicht erraten haben, definiert jquery.js während der Initialisierung eine globale Variable 'jQuery' sowie die bekanntere globale Variable '$', mit der dieser Code nach dem Einfügen von jQuery funktioniert.

Vivin Paliath
quelle
14
Sehr cool, ich verstehe den Namespace gut, aber ich habe viel von Ihrem letzten Beispiel gesehen und konnte nicht herausfinden, was die Leute erreichen wollten. Das klärt die Dinge wirklich auf.
Andrew Kou
34
Super Beitrag. Vielen Dank.
Darren
4
Ich denke, ein führendes und nachfolgendes Semikolon ';' würde das Beispiel vervollständigen - Auf ;(function(jQ) { ... code ... })(jQuery);diese Weise würde jemand, der ein Semikolon in seinem Skript weggelassen hat, Ihr Semikolon nicht beschädigen, insbesondere wenn Sie vorhaben, Ihr Skript zu minimieren und mit anderen zu verketten.
Taras Alenin
3
schöner Beitrag, ich mag die Betonung auf private Variablen. Ich mag auch die Öffnung des Modulmusters / der Abschlüsse (public_function1 & public_function2) und wie Sie Variablen übergeben, obwohl es eine nette Einführung ist, wenn Sie etwas außerhalb des Gültigkeitsbereichs liegen. Ich habe auch eine Antwort hinzugefügt, die sich auf die Wurzeln der Syntax und die Unterschiede zwischen Funktionsanweisung und Funktionsausdruck konzentriert. Ich denke, dies ist "nur eine Konvention" und "der einzige Weg, um dieses Ergebnis zu erzielen".
Adrien Be
4
Toller Beitrag, ich denke vielleicht mehr darüber, wie vorteilhaft es ist, Variablen an die selbstausführende Funktion zu übergeben. Der Kontext in der selbstausführenden Funktion ist sauber - keine Daten. Auf diese Weise (function (context) { ..... })(this)können Sie den Kontext übergeben, wodurch Sie dem übergeordneten Kontext alles hinzufügen können, was Sie möchten, und ihn so verfügbar machen.
Callum Linington
79

Zusamenfassend

Zusammenfassung

In ihrer einfachsten Form zielt diese Technik darauf ab, Code in einen Funktionsbereich einzubinden .

Es hilft, die Chancen zu verringern von:

  • Kollision mit anderen Anwendungen / Bibliotheken
  • Verschmutzung überlegener (weltweit wahrscheinlichster) Anwendungsbereich

Es ist nicht festzustellen , wann das Dokument fertig ist - es ist nicht irgendeine Art von document.onloadnochwindow.onload

Es ist allgemein als Immediately Invoked Function Expression (IIFE)oder bekannt 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 ist jede in der Funktion definierte Variable (dh deklariert mit var) "privat" und NUR innerhalb des Funktionsumfangs 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 Funktionsumfang. "In einer Funktion definierte Parameter und Variablen sind außerhalb der Funktion nicht sichtbar, und eine an einer beliebigen Stelle innerhalb einer Funktion definierte Variable ist überall in der Funktion sichtbar." (aus "Javascript: The Good Parts").


Mehr Details

Alternativer Code

Am Ende könnte der zuvor veröffentlichte 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 dachte wahrscheinlich jemand: "Es muss eine Möglichkeit geben, die Benennung von 'myMainFunction' zu vermeiden, da wir sie nur sofort ausführen möchten."

Wenn Sie zu den Grundlagen zurückkehren, stellen Sie Folgendes fest:

  • expression: etwas, das einen Wert ergibt. dh3+11/x
  • statement: Codezeile (n), die etwas tun, ABER es wird kein Wert ausgewertet. dhif(){}

In ähnlicher Weise werden Funktionsausdrücke zu einem Wert ausgewertet. Und eine Konsequenz (nehme ich an?) Ist, dass sie sofort aufgerufen werden können:

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

So wird unser komplexeres Beispiel:

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, wie unten:

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

Siehe Live-Demo .

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

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

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

Die Antwort lautet "Sie können", und es gibt tatsächlich viele Möglichkeiten, wie Sie dies tun können: Hinzufügen von a +, a !, a -oder vielleicht in Klammern setzen (wie es jetzt durch Konvention geschieht), und mehr, glaube ich. Zum Beispiel:

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

oder

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

oder

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

Sobald die relevante Änderung zu unserem früheren "alternativen Code" hinzugefügt wurde, kehren wir zu genau demselben Code zurück, der im Beispiel "Code Explained" verwendet wurde

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:


Bereiche entmystifizieren

Eine Sache, die man sich fragen könnte, ist: "Was passiert, wenn Sie die Variable NICHT 'richtig' innerhalb der Funktion definieren - dh stattdessen eine einfache Zuweisung vornehmen?"

(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 "die Bereichskette nachgeschlagen, bis sie die Variable findet oder den globalen Bereich erreicht (an welchem ​​Punkt sie erstellt wird)".

In einer Browserumgebung (im Vergleich zu einer Serverumgebung wie nodejs) wird der globale Bereich durch das windowObjekt definiert . Daher können wir tun window.myOtherFunction().

Mein "Good Practices" -Tipp zu diesem Thema lautet: Verwenden Sie immer, varwenn Sie etwas definieren : ob es sich um eine Zahl, ein Objekt oder eine Funktion handelt, und selbst wenn Sie sich im globalen Bereich befinden. Dies macht den Code viel einfacher.

Hinweis:

  • Javascript ist nicht hat block scope(Update: Block Umfang lokale in dem Nachspiel Variablen ES6 .)
  • Javascript hat nur function scope& global scope( windowGültigkeitsbereich in einer Browser-Umgebung)

Lesen Sie mehr über Javascript Scopes:


Ressourcen


Nächste Schritte

Sobald Sie dieses IIFEKonzept erhalten haben, führt es zu dem module pattern, was üblicherweise durch Nutzung dieses IIFE-Musters erfolgt. Habe Spaß :)

Adrien Be
quelle
Sehr hilfreich. Vielen Dank!
Christoffer Helgelin Hald
Schön, ich bevorzuge die Demoversion :)
Fabrizio Bertoglio
So eine tolle Erklärung. Vielen Dank!
Vikram Khemlani
26

Javascript in einem Browser hat wirklich nur ein paar effektive Bereiche: Funktionsumfang und globaler Bereich.

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

Gareth
quelle
1
Aber bietet die Konstruktorfunktion selbst keinen Spielraum für ihre eigenen Variablen?
Andrew Kou
1
Ja, jede in dieser Bibliothek definierte Funktion könnte ihre eigenen lokalen Variablen definieren, aber dies ermöglicht die gemeinsame Nutzung von Variablen zwischen den Funktionen, ohne dass diese außerhalb der Bibliothek auslaufen
Gareth
@Gareth, so ermöglicht dies "globale" Variablen innerhalb eines Bereichs (;
Francisco Presencia
2
@FranciscoPresencia "global in einem Bereich" ist kein nützlicher Ausdruck, da dies im Grunde genau das ist, was "Bereich" bedeutet. Der springende Punkt des "globalen" Bereichs ist, dass es speziell der Bereich ist, auf den alle anderen Bereiche Zugriff haben.
Gareth
19

Das nennt man eine Schließung. Grundsätzlich wird der Code innerhalb der Funktion versiegelt, damit andere Bibliotheken ihn nicht stören. Es ähnelt dem Erstellen eines Namespace in kompilierten Sprachen.

Beispiel. Angenommen, ich schreibe:

(function() {

    var x = 2;

    // do stuff with x

})();

Jetzt können andere Bibliotheken nicht auf die Variable zugreifen, die xich zur Verwendung in meiner Bibliothek erstellt habe.

Joel
quelle
7
Gehen Sie vorsichtig mit Ihrer Terminologie um. Namespacing bedeutet, dass auf die Variablen von außen zugegriffen werden kann, indem der Namespace adressiert wird (normalerweise unter Verwendung eines Präfixes). Während dies in Javascript möglich ist, ist dies nicht das, was hier gezeigt wird
Gareth
Ich bin damit einverstanden, dass es nicht genau wie ein Namespace ist. Sie können jedoch ähnliche Funktionen bereitstellen, indem Sie ein Objekt mit Eigenschaften zurückgeben, die Sie veröffentlichen möchten : (function(){ ... return { publicProp1: 'blah' }; })();. Offensichtlich nicht perfekt parallel zum Namespace, aber es kann hilfreich sein, dies so zu sehen.
Joel
In Ihrem Beispiel ist x immer noch eine private Variable ... Obwohl Sie es in ein IIFE verpackt haben. Gehen Sie voran und versuchen Sie, auf x außerhalb der Funktion zuzugreifen, Sie können nicht ..
RayLoveless
Ihr Punkt ist ungültig. Selbst in der folgenden Funktion können andere Bibliotheken nicht auf x zugreifen. function () {var x = 2}
RayLoveless
@ RayLoveless Ich stimme zu. Ich widerspreche dieser Behauptung nicht. Tatsächlich habe ich dieselbe Behauptung aufgestellt wie der letzte Satz dieser Antwort.
Joel
8

Sie können Funktionsabschlüsse auch als Daten in größeren Ausdrücken verwenden, wie bei dieser Methode zum Bestimmen 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;
     })()
    }
kennebec
quelle
Was macht das !! tun?
1,21 Gigawatt
!! konvertiert einen Wert in seine boolesche Darstellung (wahr / falsch).
Liam
7

Wenn Sie eine Bibliothek mit einer globalen Variablen schreiben, können Sie nicht nur die Variablen lokal halten, sondern auch einen kürzeren Variablennamen für die Bibliothek verwenden. Es wird häufig beim Schreiben von jQuery-Plugins verwendet, da Sie mit jQuery die auf jQuery verweisende Variable $ mit jQuery.noConflict () deaktivieren können. Falls es deaktiviert ist, kann Ihr Code weiterhin $ verwenden und nicht brechen, wenn Sie nur Folgendes tun:

(function($) { ...code...})(jQuery);
Coronus
quelle
3
  1. Um Konflikte mit anderen Methoden / Bibliotheken im selben Fenster zu vermeiden,
  2. Vermeiden Sie den globalen Bereich, machen Sie ihn zum lokalen Bereich.
  3. Um das Debuggen zu beschleunigen (lokaler Bereich),
  4. JavaScript hat nur Funktionsumfang, daher hilft es auch beim Kompilieren von Codes.
Vivek Mehta
quelle
1

Wir sollten auch 'use strict' in der Scope-Funktion verwenden, um sicherzustellen, dass der Code im "strict mode" ausgeführt wird. Beispielcode unten gezeigt

(function() {
    'use strict';

    //Your code from here
})();
Neha Jain
quelle
Warum sollten wir streng verwenden?
nbro
Überprüfen Sie diesen Artikel: stackoverflow.com/questions/1335851/…
Neha Jain
Beantwortet die Frage nicht wirklich!
Pritam Banerjee
Pritam, es ist eine gute Übung. Bitte recherchieren Sie richtig, bevor Sie eine Antwort
Neha Jain
1
'use strict' rettet schlechte Programmierer vor sich selbst. Und da die Mehrheit der Programmierer schlechte Programmierer sind, hilft dies, sie daran zu hindern, Dinge zu tun, die sie definitiv nicht tun sollten, und in einem schnell sinkenden Code-Chaos zu enden.
MattE