Wie funktioniert das Konstrukt (function () {}) () und warum wird es verwendet?

79

(function() {})()und sein jQuery-spezifischer Cousin (function($) {})(jQuery)tauchen ständig im Javascript-Code auf.

Wie funktionieren diese Konstrukte und welche Probleme lösen sie?

Beispiele geschätzt

Tom Lehman
quelle
2
Es ist kein genaues Duplikat. Die Antworten auf die anderen Fragen sagen nicht wirklich aus, warum man jQueryals Argument liefert .
Georg Schölly
1
Ich habe mich immer gefragt, warum die zusätzliche Klammer benötigt wird. Ich glaube, es hat mit einer Mehrdeutigkeit zwischen Blöcken und Objektliteralen mit den geschweiften Klammern zu tun. Beachten Sie, dass sie nicht erforderlich sind, wenn Sie etwas zuweisen, z. B. var x = function () {} () funktioniert einwandfrei.
Jpsimons
1
Warum ist dies "Geschlossen als keine wirkliche Frage"? Die Frage ist klar und relevant.
Chris

Antworten:

73

Mit der zunehmenden Beliebtheit von JavaScript-Frameworks wurde das $Zeichen bei vielen verschiedenen Gelegenheiten verwendet. Um mögliche Konflikte zu vermeiden, können Sie folgende Konstrukte verwenden:

(function ($){
  // Your code using $ here.
})(jQuery);

Dies ist insbesondere eine anonyme Funktionsdeklaration, die sofort ausgeführt wird, indem das Hauptobjekt jQuery als Parameter übergeben wird. Innerhalb dieser Funktion können Sie $auf dieses Objekt verweisen, ohne sich Gedanken darüber machen zu müssen, ob andere Frameworks ebenfalls in den Geltungsbereich fallen.

Seb
quelle
4
Kurze und aussagekräftige Antwort. +1!
Pestaa
5
Es ist technisch gesehen ein Funktionsausdruck (! Deklaration). IIFE
John Strickler
Können Sie erklären, was Sie damit meinen: "... ohne sich Sorgen zu machen, dass auch andere Frameworks in den Geltungsbereich fallen."?
Redtopia
@ Redtopia. Der Javascript-Bereich wird auf Funktionsebene definiert. Alles außerhalb einer Funktion ist global. Überlegen Sie, wie viele Javascript auf einer Webseite enthalten sind. Jede Variable außerhalb eines Funktionsumfangs ist kollisionsgefährdet.
Alberto De Caro
Vielen Dank für die Klarstellung ... mir war nicht klar, dass Sie sich auf den lokalen Bereich der anonymen Funktion beziehen, der aus dem globalen Bereich oder dem Bereich einer anderen Funktion nicht ersichtlich ist.
Redtopia
41

Dies ist eine Technik, mit der der variable Umfang begrenzt wird. Nur so kann verhindert werden, dass Variablen den globalen Namespace verschmutzen.

var bar = 1; // bar is now part of the global namespace
alert(bar);

(function () {
   var foo = 1; // foo has function scope
   alert(foo); 
   // code to be executed goes here
})();
Daniel LeCheminant
quelle
7
... und sie immer noch "global" in Bezug auf Ihren Code sein.
Tvanfosson
Der Verweis auf Opera im Beispielcode ist also irrelevant / falsch?
Bobby Jack
1
@Bobby: In diesem Block befinden sich möglicherweise zusätzliche Variablen / Funktionen, die nur für Opera verwendet werden.
Geowa4
19

1) Es definiert eine anonyme Funktion und führt sie sofort aus.

2) Dies geschieht normalerweise, um den globalen Namespace nicht mit unerwünschtem Code zu verschmutzen.

3) Sie müssen einige Methoden daraus verfügbar machen. Alles, was darin deklariert ist, ist "privat", zum Beispiel:

MyLib = (function(){
    // other private stuff here
    return {
        init: function(){
        }
    };

})();

Oder alternativ:

MyLib = {};
(function({
    MyLib.foo = function(){
    }
}));

Der Punkt ist, es gibt viele Möglichkeiten, wie Sie es verwenden können, aber das Ergebnis bleibt gleich.

Evan Trimboli
quelle
1. Eine solche Funktion ist in einer separaten Datei definiert. Wie wird sie ausgeführt? (Es scheint, dass niemand diese Funktion aufruft, also wie wird die Funktion ausgelöst?) 2. Ich habe nicht gesehen, dass die Funktion auf der obersten Ebene eine "return" -Anweisung hat ... Ich lese dojo.util._doh ._browserRunner.js Danke!
Paul
1) Wie gesagt, es führt sich selbst aus, das letzte () fordert es auf, es auszuführen. 2) Es muss nicht unbedingt eine Rückgabe sein, es kann auch Werte für eine bestimmte Eigenschaft festlegen. Es war nur ein Beispiel, ich habe die Antwort mit einer anderen Möglichkeit aktualisiert.
Evan Trimboli
9

Es ist nur eine anonyme Funktion, die sofort aufgerufen wird. Sie können die Funktion zuerst erstellen und dann aufrufen, und Sie erhalten den gleichen Effekt:

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

arbeitet als:

temp = function(){ ... };
temp();

Sie können dasselbe auch mit einer benannten Funktion tun:

function temp() { ... }
temp();

Der Code, den Sie als jQuery-spezifisch bezeichnen, ist nur in dem Sinne, dass Sie das jQuery-Objekt darin verwenden. Es ist nur eine anonyme Funktion mit einem Parameter, der sofort aufgerufen wird.

Sie können dasselbe in zwei Schritten tun, und Sie können es mit beliebigen Parametern tun:

temp = function(answer){ ... };
temp(42);

Das Problem, das dadurch gelöst wird, besteht darin, dass eine Schließung für den Code in der Funktion erstellt wird. Sie können darin Variablen deklarieren, ohne den globalen Namespace zu verschmutzen, wodurch das Risiko von Konflikten verringert wird, wenn ein Skript zusammen mit einem anderen verwendet wird.

In dem speziellen Fall für jQuery verwenden Sie es im Kompatibilitätsmodus, in dem der Name $ nicht als Alias ​​für jQuery deklariert wird. Durch Senden des jQuery-Objekts in den Abschluss und Benennen des Parameters $ können Sie weiterhin dieselbe Syntax wie ohne Kompatibilitätsmodus verwenden.

Guffa
quelle
Wie ich in der Antwort von @ spoulson kommentiert habe, ist eine anonyme Funktion nicht dasselbe wie ein Abschluss.
Tim Down
7

Hier wird erklärt , dass Ihr erstes Konstrukt Spielraum für Variablen bietet.

Variablen werden auf Funktionsebene in Javascript festgelegt. Dies unterscheidet sich von dem, was Sie in einer Sprache wie C # oder Java gewohnt sind, in der die Variablen auf den Block beschränkt sind. Dies bedeutet, dass wenn Sie eine Variable innerhalb einer Schleife oder einer if-Anweisung deklarieren, diese für die gesamte Funktion verfügbar ist.

Wenn Sie jemals eine Variable innerhalb einer Funktion explizit erfassen müssen, können Sie dazu eine anonyme Funktion verwenden. Sie können tatsächlich eine anonyme Funktion erstellen und diese dann sofort ausführen. Alle darin enthaltenen Variablen werden auf die anonyme Funktion übertragen:

(function() {
  var myProperty = "hello world";
  alert(myProperty);
})();
alert(typeof(myProperty)); // undefined
Ewan Todd
quelle
6

Ein weiterer Grund dafür ist, Verwirrung darüber zu beseitigen, welchen Framework- $Operator Sie verwenden. Um jQuery zu erzwingen, können Sie beispielsweise Folgendes tun:

;(function($){
   ... your jQuery code here...
})(jQuery);

Durch Übergeben des $Operators als Parameter und Aufrufen in jQuery wird der $Operator innerhalb der Funktion an jQuery gebunden, selbst wenn andere Frameworks geladen sind.

Tvanfosson
quelle
Das ist eine spezifischere Variante der ursprünglichen Frage, aber es sind immer noch gute Informationen! :)
Bobby Jack
Überhaupt keine Antwort, aber ich habe sie für die großartige Idee, die Sie hatten, verbessert!
Spidey
5

Eine andere Verwendung für dieses Konstrukt besteht darin, die Werte lokaler Variablen zu "erfassen", die in einem Abschluss verwendet werden. Zum Beispiel:

for (var i = 0; i < 3; i++) {
    $("#button"+i).click(function() {
        alert(i);
    });
}

Mit dem obigen Code werden alle drei Schaltflächen mit "3" angezeigt. Auf der anderen Seite:

for (var i = 0; i < 3; i++) {
    (function(i) {
        $("#button"+i).click(function() {
            alert(i);
        });
    })(i);
}

Dadurch werden die drei Schaltflächen wie erwartet mit "0", "1" und "2" angezeigt.

Der Grund dafür ist , dass ein Verschluss einen Verweis auf seinen umschließenden Stapel hält Rahmen , der die aktuellen Werte der Variablen hält. Wenn sich diese Variablen ändern, bevor der Abschluss ausgeführt wird, werden beim Abschluss nur die neuesten Werte angezeigt, nicht die Werte, die zum Zeitpunkt der Erstellung des Abschlusses vorhanden waren. Durch Umschließen der Abschlusserstellung in eine andere Funktion wie im zweiten Beispiel oben wird der aktuelle Wert der Variablen iim Stapelrahmen der anonymen Funktion gespeichert.

Greg Hewgill
quelle
Ein kleiner Trottel, aber es ist nicht der Stapelrahmen, der geschlossen ist, sondern die umgebenden lexikalischen Bereiche. Gute Implementierungen von Verschlüssen erfassen nur die Symbole, die tatsächlich im Verschluss erwähnt werden. Einige Implementierungen erfassen einfach alles (Ruby tut dies, seien Sie also vorsichtig, was Sie in der Nähe von Verschlüssen haben). Eine Möglichkeit, über ein Objekt in OOP nachzudenken, besteht darin, die erfassten Symbole explizit zu definieren (die Mitgliedsattribute des Objekts).
KayEss
Das stimmt, aber das ist wirklich nur ein Implementierungsproblem, und konzeptionell kann man davon ausgehen, dass der Stapelrahmen das ist, worauf der Abschluss verweist. OOP-artige "Objekte" in Lisp-Familiensprachen werden alle mit Verschlüssen erstellt. Dies ist eine leistungsstarke Technik.
Greg Hewgill
Ja, obwohl, wenn Sie in Stapelrahmen denken möchten, der Abschluss eine Kette von Stapelrahmen ist, ein Rahmen für jede einschließende Funktion im lexikalischen Bereich zusammen mit dem globalen Bereich. Der lexikalische Bereich ist leichter zu überlegen (insbesondere, wenn Sie jemandem erklären, der noch keine Erfahrung mit Closures hat), da Sie nicht in eine sinnlose (und irreführende) Diskussion darüber geraten, welcher Stack-Frame Sie haben, wenn Sie mehrere Ebenen verschachtelter Funktionen haben. Der lexikalische Bereich ist einfach das, was Sie im Quellcode sehen.
KayEss
4

Dies wird als Schließung angesehen . Dies bedeutet, dass der enthaltene Code in seinem eigenen lexikalischen Bereich ausgeführt wird. Dies bedeutet, dass Sie neue Variablen und Funktionen definieren können und diese nicht mit dem im Code außerhalb des Abschlusses verwendeten Namespace kollidieren.

var i = 0;
alert("The magic number is " + i);

(function() {
   var i = 99;
   alert("The magic number inside the closure is " + i);
})();

alert("The magic number is still " + i);

Dadurch werden drei Popups generiert, die zeigen, dass die ibereits vorhandene gleichnamige Variable durch das Schließen nicht geändert wird:

  • Die magische Zahl ist 0
  • Die magische Zahl im Verschluss ist 99
  • Die magische Zahl ist immer noch 0
Spoulson
quelle
5
Das ist kein Abschluss, sondern zeigt lediglich, dass eine Funktion ihren eigenen Umfang hat. Ein Abschluss wird gebildet, wenn eine Funktion innerhalb einer anderen Funktion definiert wird und außerhalb dieser Funktion verfügbar wird , wodurch der Zugriff auf die Variablen, Funktionen und Parameter in der äußeren Funktion auch nach der Rückkehr dieser äußeren Funktion erhalten bleibt.
Tim Down
Ändern Sie einfach die "(function () {..." in "var func = function () {...}; func ();". Dort, Schließung.
Spoulson
Ich möchte nicht daran arbeiten, aber ... immer noch kein Abschluss: Es gibt keine innere Funktion außerhalb der äußeren Funktion. Eine einfache Möglichkeit, in Ihrem Beispiel einen Abschluss zu erstellen, besteht darin, eine Funktion zurückzugeben, die i alarmiert hat: var f = (function () {var i = 99; return function () {alert (i);};}) (); f ();
Tim Down
Punkt notiert. Es kommt auf die Besonderheiten der Implementierung an.
Spoulson
2

Sie werden häufig in jQuery-Plugins verwendet. Wie im Authoring-Handbuch für jQuery Plugins erläutert, sind alle darin deklarierten Variablen { }privat und nach außen nicht sichtbar, was eine bessere Kapselung ermöglicht.

Darin Dimitrov
quelle
3
{}Erstellen Sie an und für sich keinen neuen Bereich in JavaScript. Der Bereich wird durch die Funktionsdeklaration erstellt.
Annabelle
2

Wie andere gesagt haben, definieren beide anonyme Funktionen, die sofort aufgerufen werden. Im Allgemeinen verpacke ich meine JavaScript-Klassendeklarationen in diese Struktur, um einen statischen privaten Bereich für die Klasse zu erstellen. Ich kann dann konstante Daten, statische Methoden, Ereignishandler oder alles andere in diesen Bereich einfügen und es wird nur für Instanzen der Klasse sichtbar sein:

// Declare a namespace object.
window.MyLibrary = {};

// Wrap class declaration to create a private static scope.
(function() {
  var incrementingID = 0;

  function somePrivateStaticMethod() {
    // ...
  }

  // Declare the MyObject class under the MyLibrary namespace.
  MyLibrary.MyObject = function() {
    this.id = incrementingID++;
  };

  // ...MyObject's prototype declaration goes here, etc...
  MyLibrary.MyObject.prototype = {
    memberMethod: function() {
      // Do some stuff
      // Maybe call a static private method!
      somePrivateStaticMethod();
    }
  };
})();

In diesem Beispiel wird die MyObjectKlasse dem MyLibraryNamespace zugewiesen , sodass auf sie zugegriffen werden kann. incrementingIDund somePrivateStaticMethod()sind außerhalb des anonymen Funktionsumfangs nicht direkt zugänglich.

Annabelle
quelle
2

Das ist im Grunde genommen ein Namespace für Ihren JavaScript-Code.

Beispielsweise können Sie dort beliebige Variablen oder Funktionen platzieren, die von außen in diesem Bereich nicht vorhanden sind. Wenn Sie also alles darin zusammenfassen, müssen Sie sich keine Gedanken über Zusammenstöße machen.

Das ()am Ende bedeutet, sich selbst anzurufen. Sie können dort auch ein Argument hinzufügen, das zum Argument Ihrer anonymen Funktion wird. Ich mache das oft mit jQuery und du kannst sehen warum ...

(function($) {

    // Now I can use $, but it won't affect any other library like Prototype
})(jQuery);

Evan Trimboli behandelt den Rest in seiner Antwort .

Alex
quelle
1

Es ist eine selbstaufrufende Funktion. Ein bisschen wie eine Abkürzung zum Schreiben

function DoSomeStuff($)
{
}

DoSomeStuff(jQuery);
Matthew
quelle
1

Der obige Code erstellt eine anonyme Funktion in Zeile 1 und ruft sie dann in Zeile 3 mit 0 Argumenten auf. Dadurch werden alle in dieser Bibliothek definierten Funktionen und Variablen effektiv gekapselt, da auf alle Funktionen nur innerhalb dieser anonymen Funktion zugegriffen werden kann.

Dies ist eine bewährte Methode, und die Begründung dahinter besteht darin, zu vermeiden, dass der globale Namespace mit Variablen und Funktionen verschmutzt wird, die durch andere Teile von Javascript auf der gesamten Website beeinträchtigt werden könnten.

Betrachten Sie das einfache Beispiel, um zu verdeutlichen, wie die Funktion aufgerufen wird:

Wenn Sie diese einzelne Zeile von Javascript enthalten haben, wird sie automatisch aufgerufen, ohne sie explizit aufzurufen:

alert('hello');

Nehmen Sie diese Idee und wenden Sie sie auf dieses Beispiel an:

(function() {
    alert('hello')
    //anything I define in here is scoped to this function only
}) (); //here, the anonymous function is invoked

Das Endergebnis ist ähnlich, da die anonyme Funktion genau wie im vorherigen Beispiel aufgerufen wird.

wsanville
quelle
Bitte sehen Sie meine erste Frage der Kommentare an Evan ... Danke!
Paul
1
@ Paul, siehe Alex 'Antwort.
Marcel Korpel
//here, the anonymous function is invoked, einfach, aber ich habe es vorher nicht gesehen :)
Stefan
1

Da die guten Code- Antworten bereits vergeben sind :) Ich werde einen Vorschlag einbringen, um einige John Resig-Videos, Video 1 , Video 2 (Erfinder von jQuery & Master bei JavaScript) anzusehen .

Einige wirklich gute Einblicke und Antworten in den Videos.

Das habe ich gerade getan, als ich Ihre Frage sah.

John K.
quelle
0
function(){ // some code here }

ist der Weg, um eine anonyme Funktion in Javascript zu definieren. Sie können Ihnen die Möglichkeit geben, eine Funktion im Kontext einer anderen Funktion auszuführen (wo Sie diese Fähigkeit sonst möglicherweise nicht haben).

Justin Niessner
quelle