web-dev-qa-db-de.com

Wie funktionieren JavaScript-Verschlüsse?

Wie würden Sie jemandem JavaScript-Closures erklären, der die Konzepte kennt, aus denen sie bestehen (z. B. Funktionen, Variablen und dergleichen), der Closures jedoch selbst nicht versteht?

Ich habe das Schema-Beispiel auf Wikipedia gesehen, aber leider hat es nicht geholfen.

7647
e-satis

JavaScript-Verschlüsse für Anfänger

Eingereicht von Morris am Di, 2006-02-21 10:19. Von der Community bearbeitet seit.

Verschlüsse sind keine Zauberei

Auf dieser Seite werden Schließungen erläutert, damit ein Programmierer sie verstehen kann - mithilfe von funktionierendem JavaScript-Code. Es ist nicht für Gurus oder funktionierende Programmierer.

Abschlüsse sind nicht schwer zu verstehen, sobald das Kernkonzept geklärt ist. Sie sind jedoch unmöglich zu verstehen, wenn man theoretische oder akademisch orientierte Erklärungen liest!

Dieser Artikel richtet sich an Programmierer mit Programmiererfahrung in einer Standardsprache, die die folgende JavaScript-Funktion lesen können:

function sayHello(name) {
  var text = 'Hello ' + name;
  var say = function() { console.log(text); }
  say();
}
sayHello('Joe');

Zwei kurze Zusammenfassungen

  • Wenn eine Funktion (foo) andere Funktionen deklariert (bar und baz), ist die Familie der in foo erstellten lokalen Variablen nicht zerstört , wenn die Funktion beendet wird . Die Variablen werden lediglich für die Außenwelt unsichtbar. foo kann daher die Funktionen bar und baz geschickt zurückgeben und sie können weiterhin über diese abgeschlossene Variablenfamilie ("der Abschluss") lesen, schreiben und miteinander kommunizieren. mit denen sich niemand einmischen kann, auch nicht jemand, der in Zukunft wieder foo anruft.

  • Ein Abschluss ist eine Möglichkeit, erstklassige Funktionen zu unterstützen; Es ist ein Ausdruck, der auf Variablen in seinem Gültigkeitsbereich (als er zum ersten Mal deklariert wurde) verweisen, einer Variablen zugewiesen, als Argument an eine Funktion übergeben oder als Funktionsergebnis zurückgegeben werden kann.

Ein Beispiel für einen Abschluss

Der folgende Code gibt einen Verweis auf eine Funktion zurück:

function sayHello2(name) {
  var text = 'Hello ' + name; // Local variable
  var say = function() { console.log(text); }
  return say;
}
var say2 = sayHello2('Bob');
say2(); // logs "Hello Bob"

Die meisten JavaScript-Programmierer werden verstehen, wie ein Verweis auf eine Funktion an eine Variable (say2) im obigen Code zurückgegeben wird. Wenn Sie dies nicht tun, müssen Sie sich das ansehen, bevor Sie Abschlüsse lernen können. Ein Programmierer, der C verwendet, würde sich die Funktion so vorstellen, als würde er einen Zeiger auf eine Funktion zurückgeben, und dass die Variablen say und say2 jeweils ein Zeiger auf eine Funktion sind.

Es gibt einen kritischen Unterschied zwischen einem C-Zeiger auf eine Funktion und einem JavaScript-Verweis auf eine Funktion. In JavaScript können Sie sich eine Funktionsreferenzvariable so vorstellen, dass sie sowohl einen Zeiger auf eine Funktion als auch als versteckten Zeiger auf einen Abschluss enthält.

Der obige Code wird geschlossen, da die anonyme Funktion function() { console.log(text); } als innerhalb einer anderen Funktion deklariert wird, in diesem Beispiel sayHello2(). Wenn Sie in JavaScript das Schlüsselwort function in einer anderen Funktion verwenden, erstellen Sie einen Abschluss.

In C und den meisten anderen gebräuchlichen Sprachen gibt nach eine Funktion zurück, auf alle lokalen Variablen kann nicht mehr zugegriffen werden, da der Stack-Frame zerstört wird.

Wenn Sie in JavaScript eine Funktion innerhalb einer anderen Funktion deklarieren, können die lokalen Variablen der äußeren Funktion nach der Rückkehr von dieser Funktion zugänglich bleiben. Dies wird oben gezeigt, weil wir die Funktion say2() aufrufen, nachdem wir von sayHello2() zurückgekehrt sind. Beachten Sie, dass der von uns aufgerufene Code auf die Variable text verweist, die eine lokale Variable der Funktion sayHello2() war.

function() { console.log(text); } // Output of say2.toString();

An der Ausgabe von say2.toString() können wir erkennen, dass der Code auf die Variable text verweist. Die anonyme Funktion kann auf text verweisen, das den Wert 'Hello Bob' enthält, da die lokalen Variablen von sayHello2() in einem Closure geheim am Leben gehalten wurden.

Das Genie ist, dass in JavaScript eine Funktionsreferenz auch eine geheime Referenz auf die Schließung hat, in der sie erstellt wurde - ähnlich wie Delegaten ein Methodenzeiger plus eine geheime Referenz auf ein Objekt sind.

Mehr Beispiele

Aus irgendeinem Grund scheinen Verschlüsse wirklich schwer zu verstehen, wenn Sie darüber lesen, aber wenn Sie einige Beispiele sehen, wird klar, wie sie funktionieren (es hat eine Weile gedauert). Ich empfehle, die Beispiele sorgfältig durchzuarbeiten, bis Sie verstanden haben, wie sie funktionieren. Wenn Sie anfangen, Verschlüsse zu verwenden, ohne vollständig zu verstehen, wie sie funktionieren, würden Sie bald einige sehr seltsame Fehler erzeugen!

Beispiel 3

Dieses Beispiel zeigt, dass die lokalen Variablen nicht kopiert werden - sie werden als Referenz beibehalten. Es ist, als ob der Stack-Frame auch nach dem Beenden der äußeren Funktion im Speicher erhalten bleibt!

function say667() {
  // Local variable that ends up within closure
  var num = 42;
  var say = function() { console.log(num); }
  num++;
  return say;
}
var sayNumber = say667();
sayNumber(); // logs 43

Beispiel 4

Alle drei globalen Funktionen haben einen gemeinsamen Verweis auf den Abschluss same , da sie alle in einem einzigen Aufruf von setupSomeGlobals() deklariert werden.

var gLogNumber, gIncreaseNumber, gSetNumber;
function setupSomeGlobals() {
  // Local variable that ends up within closure
  var num = 42;
  // Store some references to functions as global variables
  gLogNumber = function() { console.log(num); }
  gIncreaseNumber = function() { num++; }
  gSetNumber = function(x) { num = x; }
}

setupSomeGlobals();
gIncreaseNumber();
gLogNumber(); // 43
gSetNumber(5);
gLogNumber(); // 5

var oldLog = gLogNumber;

setupSomeGlobals();
gLogNumber(); // 42

oldLog() // 5

Die drei Funktionen haben gemeinsamen Zugriff auf denselben Abschluss - die lokalen Variablen von setupSomeGlobals(), als die drei Funktionen definiert wurden.

Beachten Sie, dass im obigen Beispiel, wenn Sie setupSomeGlobals() erneut aufrufen, ein neuer Abschluss (Stack-Frame!) Erstellt wird. Die alten Variablen gLogNumber, gIncreaseNumber, gSetNumber werden mit den Funktionen new überschrieben, die den neuen Abschluss haben. (Wenn Sie in JavaScript eine Funktion in einer anderen Funktion deklarieren, werden die inneren Funktionen erneut erstellt jedes Mal wenn die äußere Funktion aufgerufen wird.)

Beispiel 5

Dieses Beispiel zeigt, dass der Abschluss alle lokalen Variablen enthält, die vor dem Beenden in der äußeren Funktion deklariert wurden. Beachten Sie, dass die Variable alice tatsächlich nach der anonymen Funktion deklariert wird. Die anonyme Funktion wird zuerst deklariert, und wenn diese Funktion aufgerufen wird, kann sie auf die Variable alice zugreifen, da alice sich im gleichen Bereich befindet (JavaScript führt variables Heben aus). Außerdem ruft sayAlice()() nur direkt die Funktionsreferenz auf, die von sayAlice() zurückgegeben wurde - es ist genau das Gleiche wie zuvor, jedoch ohne die temporäre Variable.

function sayAlice() {
    var say = function() { console.log(alice); }
    // Local variable that ends up within closure
    var alice = 'Hello Alice';
    return say;
}
sayAlice()();// logs "Hello Alice"

Tricky: Beachten Sie, dass sich die Variable say auch innerhalb des Closures befindet und von jeder anderen Funktion aufgerufen werden kann, die in sayAlice() deklariert wurde, oder dass innerhalb der inside-Funktion rekursiv darauf zugegriffen werden kann.

Beispiel 6

Dies ist ein echtes Problem für viele Menschen, daher müssen Sie es verstehen. Seien Sie sehr vorsichtig, wenn Sie eine Funktion innerhalb einer Schleife definieren: Die lokalen Variablen aus dem Abschluß verhalten sich möglicherweise nicht so, wie Sie vielleicht zuerst denken.

Sie müssen die Funktion "Variables Heben" in Javascript verstehen, um dieses Beispiel zu verstehen.

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + i;
        result.Push( function() {console.log(item + ' ' + list[i])} );
    }
    return result;
}

function testList() {
    var fnlist = buildList([1,2,3]);
    // Using j only to help prevent confusion -- could use i.
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

 testList() //logs "item2 undefined" 3 times

Die Zeile result.Push( function() {console.log(item + ' ' + list[i])} fügt dem Ergebnisarray dreimal einen Verweis auf eine anonyme Funktion hinzu. Wenn Sie mit anonymen Funktionen nicht so vertraut sind, denken Sie daran:

pointer = function() {console.log(item + ' ' + list[i])};
result.Push(pointer);

Beachten Sie, dass beim Ausführen des Beispiels "item2 undefined" dreimal protokolliert wird! Dies liegt daran, dass es genau wie in den vorherigen Beispielen nur einen Abschluss für die lokalen Variablen für buildList gibt (die result, i, list und item sind). . Wenn die anonymen Funktionen in der Zeile fnlist[j]() aufgerufen werden; Sie verwenden alle denselben einzelnen Abschluss und verwenden den aktuellen Wert für i und item innerhalb dieses einen Abschlusses (wobei i den Wert 3 hat, weil die Schleife hatte abgeschlossen, und item hat den Wert 'item2'). Beachten Sie, dass wir von 0 indexieren, daher hat item den Wert item2. Und der i ++ erhöht i auf den Wert 3.

Es kann hilfreich sein zu sehen, was passiert, wenn eine Deklaration der Variablen item auf Blockebene (über das Schlüsselwort let) anstelle einer Deklaration von funktionsbezogenen Variablen über das Schlüsselwort var verwendet wird . Wenn diese Änderung vorgenommen wird, hat jede anonyme Funktion im Array result ihren eigenen Abschluss. Wenn das Beispiel ausgeführt wird, sieht die Ausgabe folgendermaßen aus:

item0 undefined
item1 undefined
item2 undefined

Wenn die Variable i auch mit let anstelle von var definiert wird, lautet die Ausgabe:

item0 1
item1 2
item2 3

Beispiel 7

In diesem letzten Beispiel wird bei jedem Aufruf der Hauptfunktion ein separater Abschluss erstellt.

function newClosure(someNum, someRef) {
    // Local variables that end up within closure
    var num = someNum;
    var anArray = [1,2,3];
    var ref = someRef;
    return function(x) {
        num += x;
        anArray.Push(num);
        console.log('num: ' + num +
            '; anArray: ' + anArray.toString() +
            '; ref.someVar: ' + ref.someVar + ';');
      }
}
obj = {someVar: 4};
fn1 = newClosure(4, obj);
fn2 = newClosure(5, obj);
fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4;
fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4;
obj.someVar++;
fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5;
fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;

Zusammenfassung

Wenn alles völlig unklar erscheint, ist es am besten, mit den Beispielen zu spielen. Das Lesen einer Erklärung ist viel schwieriger als das Verstehen von Beispielen. Meine Erklärungen zu Verschlüssen, Stapelrahmen usw. sind technisch nicht korrekt - sie sind grobe Vereinfachungen, um das Verständnis zu erleichtern. Sobald die Grundidee geklärt ist, können Sie die Details später aufgreifen.

Schlusspunkte:

  • Immer wenn Sie function in einer anderen Funktion verwenden, wird ein Closure verwendet.
  • Immer wenn Sie eval() in einer Funktion verwenden, wird ein Closure verwendet. Der Text, den Sie eval können, verweist auf lokale Variablen der Funktion, und in eval können Sie sogar neue lokale Variablen erstellen, indem Sie eval('var foo = …') verwenden.
  • Wenn Sie new Function(…) (den Funktionskonstruktor ) in einer Funktion verwenden, wird kein Abschluss erstellt. (Die neue Funktion kann nicht auf die lokalen Variablen der äußeren Funktion verweisen.)
  • Ein Schließen in JavaScript ist wie das Beibehalten einer Kopie aller lokalen Variablen, genau wie beim Beenden einer Funktion.
  • Es ist wahrscheinlich am besten zu denken, dass ein Abschluss immer nur ein Eintrag zu einer Funktion erstellt wird und die lokalen Variablen zu diesem Abschluss hinzugefügt werden.
  • Jedes Mal, wenn eine Funktion mit einem Abschluss aufgerufen wird, wird ein neuer Satz lokaler Variablen beibehalten (vorausgesetzt, die Funktion enthält eine Funktionsdeklaration und es wird entweder ein Verweis auf diese interne Funktion zurückgegeben oder ein externer Verweis für sie auf irgendeine Weise beibehalten ).
  • Zwei Funktionen sehen möglicherweise so aus, als hätten sie denselben Quelltext, verhalten sich jedoch aufgrund ihres „verborgenen“ Abschlusses völlig unterschiedlich. Ich glaube nicht, dass JavaScript-Code tatsächlich herausfinden kann, ob eine Funktionsreferenz eine Schließung hat oder nicht.
  • Wenn Sie versuchen, dynamische Quellcode-Änderungen vorzunehmen (zum Beispiel: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola'));), funktioniert dies nicht, wenn myFunction ein Closure ist (natürlich würden Sie niemals daran denken, Quellcode-Strings zu erstellen Substitution zur Laufzeit, aber ...).
  • Es ist möglich, Funktionsdeklarationen innerhalb von Funktionsdeklarationen innerhalb von Funktionen abzurufen ... und Sie können Abschlüsse auf mehr als einer Ebene erhalten.
  • Ich denke, normalerweise ist ein Abschluss ein Begriff sowohl für die Funktion als auch für die Variablen, die erfasst werden. Beachten Sie, dass ich diese Definition in diesem Artikel nicht verwende!
  • Ich vermute, dass sich die Schließungen in JavaScript von denen unterscheiden, die normalerweise in funktionalen Sprachen zu finden sind.

Links

Vielen Dank

Wenn Sie nur Abschlüsse gelernt haben (hier oder anderswo!), Bin ich an jeglichem Feedback von Ihnen über Änderungen interessiert, die Sie möglicherweise vorschlagen, um diesen Artikel klarer zu gestalten. Senden Sie eine E-Mail an morrisjohns.com (morris_closure @). Bitte beachten Sie, dass ich kein Guru in Bezug auf JavaScript bin - und auch nicht in Bezug auf Schließungen.


Der Originalbeitrag von Morris befindet sich im Internet Archive .

6963
Joel Anair

Immer wenn Sie das Funktionsschlüsselwort in einer anderen Funktion sehen, hat die innere Funktion Zugriff auf Variablen in der äußeren Funktion.

function foo(x) {
  var tmp = 3;

  function bar(y) {
    console.log(x + y + (++tmp)); // will log 16
  }

  bar(10);
}

foo(2);

Dies protokolliert immer 16, da bar auf das x zugreifen kann, das als Argument für foo definiert wurde, und es kann auch über tmp auf foo zugegriffen werden.

Das ist ein Abschluss. Eine Funktion muss nicht zurückgeben , um als Closure bezeichnet zu werden. Wenn Sie einfach auf Variablen außerhalb Ihres unmittelbaren lexikalischen Bereichs zugreifen, wird ein Abschluss erstellt .

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + (++tmp)); // will also log 16
  }
}

var bar = foo(2); // bar is now a closure.
bar(10);

Die obige Funktion protokolliert auch 16, da bar immer noch auf x und tmp verweisen kann, obwohl sie nicht mehr direkt im Gültigkeitsbereich enthalten sind.

Da tmp jedoch immer noch in bars Schließung herumhängt, wird es auch inkrementiert. Sie wird bei jedem Aufruf von bar inkrementiert.

Das einfachste Beispiel für einen Abschluss ist:

var a = 10;

function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

Wenn eine JavaScript-Funktion aufgerufen wird, wird ein neuer Ausführungskontext erstellt. Zusammen mit den Funktionsargumenten und dem übergeordneten Objekt empfängt dieser Ausführungskontext auch alle außerhalb deklarierten Variablen (im obigen Beispiel sowohl 'a' als auch 'b').

Es ist möglich, mehr als eine Abschlussfunktion zu erstellen, indem Sie entweder eine Liste von ihnen zurückgeben oder sie auf globale Variablen setzen. All dies bezieht sich auf dasselbex und dasselbe tmp, sie machen keine eigenen Kopien.

Hier ist die Zahl x eine wörtliche Zahl. Wie bei anderen Literalen in JavaScript wird beim Aufrufen von foo die Zahl xkopiert in foo als Argument x.

Andererseits verwendet JavaScript beim Umgang mit Objekten immer Referenzen. Wenn Sie beispielsweise foo mit einem Objekt aufgerufen haben, wird der von ihm zurückgegebene Abschluss Referenz das ursprüngliche Objekt!

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    console.log(x.memb);
  }
}

var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);

Wie erwartet wird bei jedem Aufruf von bar(10)x.memb erhöht. Was nicht erwartet werden kann, ist, dass x sich einfach auf dasselbe Objekt bezieht wie die Variable age! Nach ein paar Anrufen von bar wird age.memb 2 sein! Diese Referenzierung ist die Basis für Speicherverluste bei HTML-Objekten.

3924
Ali

VORWORT: Diese Antwort wurde geschrieben, als die Frage lautete:

Wie der alte Albert sagte: "Wenn Sie es einem Sechsjährigen nicht erklären können, verstehen Sie es selbst nicht." Nun, ich habe versucht, einem 27-jährigen Freund die Schließung von JS zu erklären, und bin völlig gescheitert.

Kann jemand annehmen, dass ich 6 bin und mich seltsamerweise für dieses Thema interessiere?

Ich bin mir ziemlich sicher, dass ich eine der wenigen Personen war, die versucht haben, die ursprüngliche Frage buchstäblich zu beantworten. Seitdem hat sich die Frage mehrmals verändert, sodass meine Antwort jetzt möglicherweise unglaublich albern und fehl am Platz erscheint. Hoffentlich bleibt die allgemeine Idee der Geschichte für einige Spaß.


Ich bin ein großer Fan von Analogie und Metapher, wenn es darum geht, schwierige Konzepte zu erklären. Lassen Sie mich also versuchen, mich mit einer Geschichte zu beschäftigen.

Es war einmal:

Es gab eine Prinzessin ...

function princess() {

Sie lebte in einer wundervollen Welt voller Abenteuer. Sie lernte ihren Prinzen Charming kennen, ritt mit einem Einhorn um ihre Welt, kämpfte mit Drachen, begegnete sprechenden Tieren und vielen anderen fantastischen Dingen.

    var adventures = [];

    function princeCharming() { /* ... */ }

    var Unicorn = { /* ... */ },
        dragons = [ /* ... */ ],
        squirrel = "Hello!";

    /* ... */

Aber sie musste immer wieder in ihre langweilige Welt der Hausarbeit und der Erwachsenen zurückkehren.

    return {

Und sie erzählte ihnen oft von ihrem neuesten erstaunlichen Abenteuer als Prinzessin.

        story: function() {
            return adventures[adventures.length - 1];
        }
    };
}

Aber alles, was sie sehen würden, ist ein kleines Mädchen ...

var littleGirl = princess();

... Geschichten über Magie und Fantasie erzählen.

littleGirl.story();

Und obwohl die Erwachsenen von echten Prinzessinnen wussten, glaubten sie niemals an die Einhörner oder Drachen, weil sie sie niemals sehen konnten. Die Erwachsenen sagten, dass sie nur innerhalb der Vorstellungskraft des kleinen Mädchens existierten.

Aber wir kennen die wahre Wahrheit; dass das kleine Mädchen mit der Prinzessin drinnen ...

... ist wirklich eine Prinzessin mit einem kleinen Mädchen im Inneren.

2368
Jacob Swartwood

Wenn wir die Frage ernst nehmen, sollten wir herausfinden, was ein typischer 6-Jähriger in der Lage ist, kognitiv, obwohl zugegebenermaßen einer, der sich für JavaScript interessiert, nicht so typisch ist.

Am Kindheitsentwicklung: 5 bis 7 Jahre heißt es:

Ihr Kind kann zweistufige Anweisungen befolgen. Wenn Sie beispielsweise zu Ihrem Kind sagen: "Gehen Sie in die Küche und holen Sie mir einen Müllsack", können sie sich an diese Anweisung erinnern.

Anhand dieses Beispiels können wir die folgenden Abschlüsse erläutern:

Die Küche ist ein Verschluss mit einer lokalen Variablen namens trashBags. In der Küche gibt es eine Funktion namens getTrashBag, die einen Müllsack erhält und zurückgibt.

Wir können dies in JavaScript wie folgt codieren:

function makeKitchen() {
  var trashBags = ['A', 'B', 'C']; // only 3 at first

  return {
    getTrashBag: function() {
      return trashBags.pop();
    }
  };
}

var kitchen = makeKitchen();

console.log(kitchen.getTrashBag()); // returns trash bag C
console.log(kitchen.getTrashBag()); // returns trash bag B
console.log(kitchen.getTrashBag()); // returns trash bag A

Weitere Punkte, die erklären, warum Verschlüsse interessant sind:

  • Jedes Mal, wenn makeKitchen() aufgerufen wird, wird ein neuer Abschluss mit einem eigenen separaten trashBags erstellt.
  • Die Variable trashBags befindet sich lokal im Inneren jeder Küche und ist außerhalb nicht zugänglich, aber die innere Funktion der Eigenschaft getTrashBag hat Zugriff darauf.
  • Jeder Funktionsaufruf erstellt einen Verschluss, aber es wäre nicht erforderlich, den Verschluss in der Nähe zu halten, es sei denn, eine innere Funktion, die Zugriff auf das Innere des Verschlusses hat, kann von außerhalb des Verschlusses aufgerufen werden. Die Rückgabe des Objekts mit der Funktion getTrashBag erledigt dies hier.
727
dlaliberte

Der Strohmann

Ich muss wissen, wie oft auf eine Schaltfläche geklickt wurde, und bei jedem dritten Klick etwas tun ...

Ziemlich offensichtliche Lösung

// Declare counter outside event handler's scope
var counter = 0;
var element = document.getElementById('button');

element.addEventListener("click", function() {
  // Increment outside counter
  counter++;

  if (counter === 3) {
    // Do something every third time
    console.log("Third time's the charm!");

    // Reset counter
    counter = 0;
  }
});
<button id="button">Click Me!</button>

Dies wird nun funktionieren, greift jedoch in den äußeren Bereich ein, indem eine Variable hinzugefügt wird, deren einziger Zweck darin besteht, die Zählung zu verfolgen. In einigen Situationen ist dies vorzuziehen, da Ihre äußere Anwendung möglicherweise Zugriff auf diese Informationen benötigt. In diesem Fall ändern wir jedoch nur das Verhalten jedes dritten Klicks. Daher ist es vorzuziehen, diese Funktionalität in die Ereignisbehandlungsroutine einzufügen.

Betrachten Sie diese Option

var element = document.getElementById('button');

element.addEventListener("click", (function() {
  // init the count to 0
  var count = 0;

  return function(e) { // <- This function becomes the click handler
    count++; //    and will retain access to the above `count`

    if (count === 3) {
      // Do something every third time
      console.log("Third time's the charm!");

      //Reset counter
      count = 0;
    }
  };
})());
<button id="button">Click Me!</button>

Beachten Sie hier einige Dinge.

Im obigen Beispiel verwende ich das Schließverhalten von JavaScript. Mit diesem Verhalten kann jede Funktion auf unbestimmte Zeit auf den Bereich zugreifen, in dem sie erstellt wurde. Um dies praktisch anzuwenden, rufe ich sofort eine Funktion auf, die eine andere Funktion zurückgibt, und weil die Funktion, die ich zurückgebe, hat Zugriff auf die interne Zählvariable (aufgrund des oben erläuterten Schließverhaltens) führt zu einem privaten Verwendungsbereich für die resultierende Funktion ... Nicht so einfach? Lass es uns verdünnen ...

Ein einfacher einzeiliger Abschluss

//          _______________________Immediately invoked______________________
//         |                                                                |
//         |        Scope retained for use      ___Returned as the____      |
//         |       only by returned function   |    value of func     |     |
//         |             |            |        |                      |     |
//         v             v            v        v                      v     v
var func = (function() { var a = 'val'; return function() { alert(a); }; })();

Alle Variablen außerhalb der zurückgegebenen Funktion stehen der zurückgegebenen Funktion zur Verfügung, aber sie stehen dem zurückgegebenen Funktionsobjekt nicht direkt zur Verfügung ...

func();  // Alerts "val"
func.a;  // Undefined

Kapiert? In unserem primären Beispiel ist die Zählvariable also im Abschluss enthalten und steht dem Ereignishandler immer zur Verfügung, sodass ihr Status von Klick zu Klick erhalten bleibt.

Außerdem kann auf diesen privaten Variablenstatus vollständig zugegriffen werden, sowohl für das Lesen als auch für das Zuweisen zu den Variablen mit privatem Gültigkeitsbereich.

Es geht los; Sie kapseln dieses Verhalten jetzt vollständig ein.

Vollständiger Blog-Beitrag (einschließlich Überlegungen zu jQuery)

566
jondavidjohn

Verschlüsse sind schwer zu erklären, da sie dazu verwendet werden, ein Verhalten zum Funktionieren zu bringen, von dem jeder intuitiv erwartet, dass es trotzdem funktioniert. Ich finde den besten Weg, sie zu erklären (und wie I gelernt hat, was sie tun), indem ich mir die Situation ohne sie vorstelle:

    var bind = function(x) {
        return function(y) { return x + y; };
    }
    
    var plus5 = bind(5);
    console.log(plus5(3));

Was würde hier passieren, wenn JavaScript nicht Schließungen kennt? Ersetzen Sie einfach den Aufruf in der letzten Zeile durch seinen Methodenrumpf (was im Grunde genommen Funktionsaufrufe tun) und Sie erhalten:

console.log(x + 3);

Wo ist nun die Definition von x? Wir haben es im aktuellen Geltungsbereich nicht definiert. Die einzige Lösung besteht darin, plus5carry den Gültigkeitsbereich (oder besser gesagt den Gültigkeitsbereich der Eltern) herumzulassen. Auf diese Weise ist x genau definiert und an den Wert 5 gebunden.

476
Konrad Rudolph

OK, 6 Jahre alter Ventilator. Möchten Sie das einfachste Beispiel für die Schließung hören?

Stellen wir uns die nächste Situation vor: Ein Fahrer sitzt in einem Auto. Das Auto ist in einem Flugzeug. Flugzeug ist im Flughafen. Die Fähigkeit des Fahrers, auf Dinge außerhalb seines Autos, aber innerhalb des Flugzeugs zuzugreifen, selbst wenn dieses Flugzeug einen Flughafen verlässt, ist eine Schließung. Das ist es. Wenn Sie 27 werden, sehen Sie sich ausführlichere Erklärung oder das folgende Beispiel an.

So kann ich meine Flugzeuggeschichte in Code umwandeln.

var plane = function(defaultAirport) {

  var lastAirportLeft = defaultAirport;

  var car = {
    driver: {
      startAccessPlaneInfo: function() {
        setInterval(function() {
          console.log("Last airport was " + lastAirportLeft);
        }, 2000);
      }
    }
  };
  car.driver.startAccessPlaneInfo();

  return {
    leaveTheAirport: function(airPortName) {
      lastAirportLeft = airPortName;
    }
  }
}("Boryspil International Airport");

plane.leaveTheAirport("John F. Kennedy");
361
Max Tkachenko

Dies ist ein Versuch, einige (mögliche) Missverständnisse über Abschlüsse auszuräumen, die in einigen der anderen Antworten auftauchen.

  • Ein Abschluss wird nicht nur erstellt, wenn Sie eine innere Funktion zurückgeben. Tatsächlich muss die umschließende Funktion überhaupt nicht zurückgegeben werden , damit sein Abschluss erstellt wird. Sie können Ihre innere Funktion stattdessen einer Variablen in einem äußeren Bereich zuweisen oder sie als Argument an eine andere Funktion übergeben, wo sie sofort oder zu einem späteren Zeitpunkt aufgerufen werden kann. Daher wird der Abschluss der umschließenden Funktion wahrscheinlich erstellt , sobald die umschließende Funktion aufgerufen wird , da jede innere Funktion immer dann auf diesen Abschluss zugreifen kann, wenn die innere Funktion aktiv ist wird aufgerufen, bevor oder nachdem die einschließende Funktion zurückkehrt.
  • Ein Abschluss verweist nicht auf eine Kopie der alten Werte von Variablen in seinem Gültigkeitsbereich. Die Variablen selbst sind Teil des Abschlusses Daher ist der Wert, der beim Zugriff auf eine dieser Variablen angezeigt wird, der letzte Wert zum Zeitpunkt des Zugriffs. Aus diesem Grund können innere Funktionen, die innerhalb von Schleifen erstellt werden, schwierig sein, da jeder auf die gleichen äußeren Variablen zugreifen kann, anstatt eine Kopie der Variablen zum Zeitpunkt der Erstellung oder des Aufrufs der Funktion abzurufen.
  • Die "Variablen" in einem Closure enthalten alle benannten Funktionen deklariert innerhalb der Funktion. Sie enthalten auch Argumente der Funktion. Ein Abschluss hat auch Zugriff auf die Variablen des enthaltenen Abschlusses, bis hin zum globalen Gültigkeitsbereich.
  • Closures verwenden Speicher, verursachen jedoch keine Speicherverluste da JavaScript selbst seine eigenen kreisförmigen Strukturen bereinigt, auf die nicht verwiesen wird. Internet Explorer-Speicherverluste, an denen Abschlüsse beteiligt sind, werden erzeugt, wenn die Verbindung zu DOM-Attributwerten, die auf Abschlüsse verweisen, nicht getrennt werden kann, wodurch Verweise auf möglicherweise kreisförmige Strukturen beibehalten werden.
360
dlaliberte

Ein Abschluß ist einem Objekt sehr ähnlich. Sie wird immer dann instanziiert, wenn Sie eine Funktion aufrufen.

Der Gültigkeitsbereich eines Closure in JavaScript ist lexikalisch. Dies bedeutet, dass alles, was in der Funktion enthalten ist, zu der das Closure gehört, Zugriff auf alle darin enthaltenen Variablen hat.

Eine Variable ist in Closure enthalten, wenn Sie

  1. weisen Sie es mit var foo=1; oder zu
  2. schreibe einfach var foo;

Wenn eine innere Funktion (eine Funktion, die in einer anderen Funktion enthalten ist) auf eine solche Variable zugreift, ohne sie mit var in ihrem eigenen Gültigkeitsbereich zu definieren, ändert sie den Inhalt der Variablen im äußeren Abschluß.

A Closure überlebt die Laufzeit der Funktion, die es erzeugt hat. Wenn andere Funktionen es aus dem Closure/Scope schaffen, in dem sie definiert sind (zum Beispiel als Rückgabewerte), verweisen diese weiterhin auf Closure.

Beispiel

function example(closure) {
  // define somevariable to live in the closure of example
  var somevariable = 'unchanged';

  return {
    change_to: function(value) {
      somevariable = value;
    },
    log: function(value) {
      console.log('somevariable of closure %s is: %s',
        closure, somevariable);
    }
  }
}

closure_one = example('one');
closure_two = example('two');

closure_one.log();
closure_two.log();
closure_one.change_to('some new value');
closure_one.log();
closure_two.log();

Ausgabe

somevariable of closure one is: unchanged
somevariable of closure two is: unchanged
somevariable of closure one is: some new value
somevariable of closure two is: unchanged
357
Florian Bösch

Vor einiger Zeit schrieb ich einen Blog-Beitrag, in dem ich die Schließungen erklärte. Hier ist, was ich über Schließungen in Bezug auf , warum Sie eine möchten, gesagt habe.

Closures sind eine Möglichkeit, einer Funktion persistente private Variablen zuzuweisen - dh Variablen, die nur einer Funktion bekannt sind und in denen sie Informationen nachverfolgen kann Aus früheren Zeiten, dass es ausgeführt wurde.

In diesem Sinne lassen sie eine Funktion wie ein Objekt mit privaten Attributen wirken.

Vollständiger Beitrag:

Also, was sind diese Verschlusssachen?

231
Nathan Long

Verschlüsse sind einfach:

Das folgende einfache Beispiel behandelt alle wichtigen Punkte von JavaScript-Verschlüssen.* 

Hier ist eine Fabrik, die Taschenrechner herstellt, die addieren und multiplizieren können:

function make_calculator() {
  var n = 0; // this calculator stores a single number n
  return {
    add: function(a) {
      n += a;
      return n;
    },
    multiply: function(a) {
      n *= a;
      return n;
    }
  };
}

first_calculator = make_calculator();
second_calculator = make_calculator();

first_calculator.add(3); // returns 3
second_calculator.add(400); // returns 400

first_calculator.multiply(11); // returns 33
second_calculator.multiply(10); // returns 4000

Der entscheidende Punkt: Bei jedem Aufruf von make_calculator wird eine neue lokale Variable n erstellt, die weiterhin für die Funktionen add und multiply des Rechners verwendet werden kann nachdem make_calculator zurückgekehrt ist.

Wenn Sie mit Stack-Frames vertraut sind, scheinen diese Rechner seltsam: Wie können sie nach der Rückkehr von make_calculator weiterhin auf n zugreifen? Die Antwort ist, sich vorzustellen, dass JavaScript keine "Stack-Frames" verwendet, sondern "Heap-Frames", die nach dem Funktionsaufruf, der sie zurückgegeben hat, bestehen bleiben können.

Innere Funktionen wie add und multiply, die auf Variablen zugreifen, die in einer äußeren Funktion deklariert wurden**, heißen Verschlüsse .

Das ist so ziemlich alles, was es mit Schließungen auf sich hat.



* Beispielsweise werden alle Punkte im Artikel "Closures for Dummies" in eine weitere Antwort behandelt, mit Ausnahme von Beispiel 6, das lediglich zeigt, dass Variablen verwendet werden können, bevor sie deklariert werden aber völlig unabhängig von Verschlüssen. Es werden auch alle Punkte in die akzeptierte Antwort behandelt, mit Ausnahme der Punkte (1), mit denen Funktionen ihre Argumente in lokale Variablen kopieren (die benannten Funktionsargumente), und (2), mit denen beim Kopieren von Zahlen neue erstellt werden Wenn Sie jedoch eine Objektreferenz kopieren, erhalten Sie eine weitere Referenz auf dasselbe Objekt. Diese sind auch gut zu wissen, haben aber wiederum nichts mit Verschlüssen zu tun. Es ist auch dem Beispiel in diese Antwort sehr ähnlich, aber ein bisschen kürzer und weniger abstrakt. Es behandelt nicht den Punkt diese Antwort oder dieser Kommentar , was bedeutet, dass JavaScript es schwierig macht, den Strom zu stecken. Wert einer Schleifenvariablen in Ihre innere Funktion: Der Schritt "Einstecken" kann nur mit einer Hilfsfunktion ausgeführt werden, die Ihre innere Funktion einschließt und bei jeder Schleifeniteration aufgerufen wird. (Genau genommen greift die innere Funktion auf die Kopie der Variablen der Hilfsfunktion zu, anstatt dass etwas eingesteckt ist.) Auch dies ist sehr nützlich, wenn Sie Abschlüsse erstellen, aber nicht Teil dessen, was ein Abschluss ist oder wie er funktioniert. Es gibt zusätzliche Verwirrung aufgrund der unterschiedlichen Funktionsweise von Closures in funktionalen Sprachen wie ML, bei denen Variablen eher an Werte als an Speicherplatz gebunden sind, was einen konstanten Strom von Menschen bereitstellt, die Closures auf eine Art und Weise verstehen (nämlich "Plug-in") Einfach falsch für JavaScript, bei dem Variablen immer an Speicherplatz und niemals an Werte gebunden sind.

** Jede äußere Funktion, wenn mehrere verschachtelt sind, oder sogar im globalen Kontext, wie diese Antwort deutlich macht.

212
Matt

Wie ich es einem Sechsjährigen erklären würde:

Sie wissen, wie Erwachsene ein Haus besitzen können, und sie nennen es zu Hause? Wenn eine Mutter ein Kind hat, besitzt das Kind eigentlich nichts, oder? Aber seine Eltern besitzen ein Haus, und wenn jemand das Kind fragt, wo es zu Hause ist, kann es ihm antworten und auf das Haus seiner Eltern zeigen. Eine "Schließung" ist die Fähigkeit des Kindes, immer (auch wenn es im Ausland ist) sagen zu können, dass es ein Zuhause hat, obwohl es wirklich den Eltern gehört, die das Haus besitzen.

203
Magne

Können Sie einem 5-Jährigen die Schließung erklären? *

Ich denke immer noch Erklärung von Google funktioniert sehr gut und ist prägnant:

/*
*    When a function is defined in another function and it
*    has access to the outer function's context even after
*    the outer function returns.
*
* An important concept to learn in JavaScript.
*/

function outerFunction(someNum) {
    var someString = 'Hey!';
    var content = document.getElementById('content');
    function innerFunction() {
        content.innerHTML = someNum + ': ' + someString;
        content = null; // Internet Explorer memory leak for DOM reference
    }
    innerFunction();
}

outerFunction(1);​

Proof that this example creates a closure even if the inner function doesn't return

* Eine C # Frage

195
Chris S

Ich neige dazu, durch gute/schlechte Vergleiche besser zu lernen. Ich sehe gerne Arbeitscode, gefolgt von nicht funktionierendem Code, auf den wahrscheinlich jemand stößt. Ich habe eine jsFiddle zusammengestellt, die einen Vergleich anstellt und versucht, die Unterschiede auf die einfachsten Erklärungen zu reduzieren, die mir einfallen könnten.

Verschlüsse richtig gemacht:

console.log('CLOSURES DONE RIGHT');

var arr = [];

function createClosure(n) {
    return function () {
        return 'n = ' + n;
    }
}

for (var index = 0; index < 10; index++) {
    arr[index] = createClosure(index);
}

for (var index in arr) {
    console.log(arr[index]());
}
  • Im obigen Code wird createClosure(n) in jeder Iteration der Schleife aufgerufen. Beachten Sie, dass ich die Variable n benannt habe, um hervorzuheben, dass es sich um eine neue Variable handelt, die in einem neuen Funktionsumfang erstellt wurde und nicht dieselbe Variable ist wie index, das an den äußeren Geltungsbereich gebunden ist.

  • Dadurch wird ein neuer Bereich erstellt, und n ist an diesen Bereich gebunden. Dies bedeutet, dass wir 10 separate Bereiche haben, einen für jede Iteration.

  • createClosure(n) gibt eine Funktion zurück, die das n innerhalb dieses Bereichs zurückgibt.

  • Innerhalb jedes Gültigkeitsbereichs ist n an den Wert gebunden, den es hatte, als createClosure(n) aufgerufen wurde, sodass die verschachtelte Funktion, die zurückgegeben wurde, immer den Wert von n zurückgibt, den es hatte, als createClosure(n) wurde aufgerufen.

Verschlüsse falsch gemacht:

console.log('CLOSURES DONE WRONG');

function createClosureArray() {
    var badArr = [];

    for (var index = 0; index < 10; index++) {
        badArr[index] = function () {
            return 'n = ' + index;
        };
    }
    return badArr;
}

var badArr = createClosureArray();

for (var index in badArr) {
    console.log(badArr[index]());
}
  • Im obigen Code wurde die Schleife innerhalb der Funktion createClosureArray() verschoben und die Funktion gibt jetzt nur das fertige Array zurück, was auf den ersten Blick intuitiver zu sein scheint.

  • Was möglicherweise nicht offensichtlich ist, ist, dass createClosureArray() nur dann aufgerufen wird, wenn nur ein Bereich für diese Funktion erstellt wurde, und nicht einer für jede Iteration der Schleife.

  • Innerhalb dieser Funktion wird eine Variable mit dem Namen index definiert. Die Schleife wird ausgeführt und fügt dem Array Funktionen hinzu, die index zurückgeben. Beachten Sie, dass index in der Funktion createClosureArray definiert ist, die immer nur einmal aufgerufen wird.

  • Da die Funktion createClosureArray() nur einen Bereich enthielt, ist index nur an einen Wert innerhalb dieses Bereichs gebunden. Mit anderen Worten, jedes Mal, wenn die Schleife den Wert von index ändert, ändert sie ihn für alle Verweise innerhalb dieses Bereichs.

  • Alle dem Array hinzugefügten Funktionen geben die Variable SAME index aus dem übergeordneten Bereich zurück, in dem sie anstelle von 10 verschiedenen aus 10 verschiedenen Bereichen wie im ersten Beispiel definiert wurde. Das Endergebnis ist, dass alle 10 Funktionen dieselbe Variable aus demselben Bereich zurückgeben.

  • Nachdem die Schleife beendet und index geändert wurde, war der Endwert 10, daher gibt jede zum Array hinzugefügte Funktion den Wert der einzelnen index -Variablen zurück, die jetzt auf 10 gesetzt ist.

Ergebnis

SCHLIESSUNGEN RICHTIG GEMACHT
n = 0
n = 1
n = 2
n = 3
n = 4
n = 5
n = 6
n = 7
n = 8
n = 9

SCHLIESSUNGEN FALSCH GEMACHT
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10

169
Chev

Wikipedia über Schließungen :

In der Informatik ist ein Abschluß eine Funktion zusammen mit einer Referenzierungsumgebung für die nichtlokalen Namen (freien Variablen) dieser Funktion.

Technisch gesehen ist in JavaScript jede Funktion ein Abschluß . Es hat immer Zugriff auf Variablen, die im umgebenden Bereich definiert sind.

Da die bereichsdefinierende Konstruktion in JavaScript eine Funktion ist, kein Codeblock wie in vielen anderen Sprachen , was wir In der Regel ist mit closure in JavaScript eine -Funktion gemeint, die mit nichtlokalen Variablen arbeitet, die in bereits ausgeführten Umgebungsfunktionen definiert sind .

Closures werden häufig zum Erstellen von Funktionen mit versteckten privaten Daten verwendet (dies ist jedoch nicht immer der Fall).

var db = (function() {
    // Create a hidden object, which will hold the data
    // it's inaccessible from the outside.
    var data = {};

    // Make a function, which will provide some access to the data.
    return function(key, val) {
        if (val === undefined) { return data[key] } // Get
        else { return data[key] = val } // Set
    }
    // We are calling the anonymous surrounding function,
    // returning the above inner function, which is a closure.
})();

db('x')    // -> undefined
db('x', 1) // Set x to 1
db('x')    // -> 1
// It's impossible to access the data object itself.
// We are able to get or set individual it.

ems

Das obige Beispiel verwendet eine anonyme Funktion, die einmal ausgeführt wurde. Muss aber nicht sein. Es kann benannt (z. B. mkdb) und später ausgeführt werden, wobei bei jedem Aufruf eine Datenbankfunktion generiert wird. Jede generierte Funktion hat ein eigenes verstecktes Datenbankobjekt. Ein weiteres Verwendungsbeispiel für Closures ist, wenn wir keine Funktion zurückgeben, sondern ein Objekt, das mehrere Funktionen für verschiedene Zwecke enthält, wobei jede dieser Funktionen Zugriff auf dieselben Daten hat.

161
mykhal

Ich habe ein interaktives JavaScript-Tutorial zusammengestellt, um zu erklären, wie Verschlüsse funktionieren. Was ist eine Schließung?

Hier ist eines der Beispiele:

var create = function (x) {
    var f = function () {
        return x; // We can refer to x here!
    };
    return f;
};
// 'create' takes one argument, creates a function

var g = create(42);
// g is a function that takes no arguments now

var y = g();
// y is 42 here
135

Die Kinder werden sich immer an die Geheimnisse erinnern, die sie mit ihren Eltern geteilt haben, auch nachdem ihre Eltern gegangen sind. Dies sind Verschlüsse für Funktionen.

Die Geheimnisse für JavaScript-Funktionen sind die privaten Variablen

var parent = function() {
 var name = "Mary"; // secret
}

Jedes Mal, wenn Sie es aufrufen, wird die lokale Variable "name" erstellt und der Name "Mary" gegeben. Und jedes Mal, wenn die Funktion beendet wird, geht die Variable verloren und der Name wird vergessen.

Da die Variablen bei jedem Aufruf der Funktion neu erstellt werden und niemand sonst sie kennt, muss es einen geheimen Ort geben, an dem sie gespeichert werden. Man könnte es Kammer der Geheimnisse oder Stapel oder lokaler Geltungsbereich nennen, aber es spielt keine Rolle. Wir wissen, dass sie irgendwo im Gedächtnis versteckt sind.

In JavaScript gibt es jedoch das Besondere, dass Funktionen, die in anderen Funktionen erstellt werden, auch die lokalen Variablen ihrer Eltern kennen und sie so lange behalten können, wie sie leben.

var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    // I can also see that "name" is "Mary"
  }
}

Solange wir uns also in der Parent-Funktion befinden, können eine oder mehrere Child-Funktionen erstellt werden, die die geheimen Variablen vom geheimen Ort aus gemeinsam nutzen.

Aber das Traurige ist, wenn das Kind auch eine private Variable seiner Elternfunktion ist, würde es auch sterben, wenn das Elternteil endet, und die Geheimnisse würden mit ihnen sterben.

Um zu leben, muss das Kind gehen, bevor es zu spät ist

var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    return "My name is " + childName  +", child of " + name; 
  }
  return child; // child leaves the parent ->
}
var child = parent(); // < - and here it is outside 

Und jetzt, obwohl Mary "nicht mehr rennt", geht die Erinnerung an sie nicht verloren und ihr Kind wird sich immer an ihren Namen und andere Geheimnisse erinnern, die sie während ihrer gemeinsamen Zeit geteilt haben.

Wenn Sie das Kind also "Alice" nennen, wird es antworten

child("Alice") => "My name is Alice, child of Mary"

Das ist alles was es zu erzählen gibt.

125
Tero Tolonen

Ich verstehe nicht, warum die Antworten hier so komplex sind.

Hier ist eine Schließung:

var a = 42;

function b() { return a; }

Ja. Sie verwenden das wahrscheinlich mehrmals am Tag.


Es gibt keinen Grund zu der Annahme, dass Verschlüsse ein komplexer Design-Hack sind, um bestimmte Probleme anzugehen. Nein, bei Abschlüssen wird nur eine Variable verwendet, die aus einem höheren Bereich stammt aus der Perspektive, in der die Funktion deklariert wurde (nicht ausgeführt).

Was es Ihnen jetzt ermöglicht , kann spektakulärer sein, siehe andere Antworten.

102
floribon

Beispiel für den ersten Punkt von dlaliberte:

Ein Abschluss wird nicht nur erstellt, wenn Sie eine innere Funktion zurückgeben. Tatsächlich muss die umschließende Funktion überhaupt nicht zurückkehren. Sie können Ihre innere Funktion stattdessen einer Variablen in einem äußeren Bereich zuweisen oder sie als Argument an eine andere Funktion übergeben, wo sie sofort verwendet werden kann. Daher existiert die Schließung der einschließenden Funktion wahrscheinlich bereits zu dem Zeitpunkt, an dem die einschließende Funktion aufgerufen wurde, da jede innere Funktion auf sie zugreifen kann, sobald sie aufgerufen wird.

var i;
function foo(x) {
    var tmp = 3;
    i = function (y) {
        console.log(x + y + (++tmp));
    }
}
foo(2);
i(3);
91
someisaac

Bei einem Abschluss hat eine innere Funktion Zugriff auf Variablen in ihrer äußeren Funktion. Dies ist wahrscheinlich die einfachste einzeilige Erklärung für Schließungen.

86
Rakesh Pai

Ich weiß, dass es bereits viele Lösungen gibt, aber ich denke, dass dieses kleine und einfache Skript hilfreich sein kann, um das Konzept zu demonstrieren:

// makeSequencer will return a "sequencer" function
var makeSequencer = function() {
    var _count = 0; // not accessible outside this function
    var sequencer = function () {
        return _count++;
    }
    return sequencer;
}

var fnext = makeSequencer();
var v0 = fnext();     // v0 = 0;
var v1 = fnext();     // v1 = 1;
var vz = fnext._count // vz = undefined
85
Gerardo Lima

Du schläfst vorbei und lädst Dan ein. Sie fordern Dan auf, einen XBox-Controller mitzubringen.

Dan lädt Paul ein. Dan bittet Paul, einen Controller mitzubringen. Wie viele Controller wurden zur Party gebracht?

function sleepOver(howManyControllersToBring) {

    var numberOfDansControllers = howManyControllersToBring;

    return function danInvitedPaul(numberOfPaulsControllers) {
        var totalControllers = numberOfDansControllers + numberOfPaulsControllers;
        return totalControllers;
    }
}

var howManyControllersToBring = 1;

var inviteDan = sleepOver(howManyControllersToBring);

// The only reason Paul was invited is because Dan was invited. 
// So we set Paul's invitation = Dan's invitation.

var danInvitedPaul = inviteDan(howManyControllersToBring);

alert("There were " + danInvitedPaul + " controllers brought to the party.");
81
StewShack

JavaScript-Funktionen können auf Folgendes zugreifen:

  1. Argumente
  2. Lokale (dh ihre lokalen Variablen und lokalen Funktionen)
  3. Umwelt, einschließlich:
    • globals, einschließlich des DOM
    • alles in äußeren Funktionen

Wenn eine Funktion auf ihre Umgebung zugreift, ist die Funktion eine Schließung.

Beachten Sie, dass äußere Funktionen nicht erforderlich sind, obwohl sie Vorteile bieten, auf die ich hier nicht näher eingehen möchte. Durch den Zugriff auf Daten in seiner Umgebung werden diese Daten durch einen Abschluss am Leben erhalten. Im Unterfall von äußeren/inneren Funktionen kann eine äußere Funktion lokale Daten erstellen und schließlich beenden. Wenn jedoch eine oder mehrere innere Funktionen nach dem Beenden der äußeren Funktion bestehen bleiben, behalten die inneren Funktionen die lokalen Daten der äußeren Funktion bei am Leben.

Beispiel für einen Abschluss, der die globale Umgebung verwendet:

Stellen Sie sich vor, dass die Schaltflächenereignisse Stack Overflow Vote-Up und Vote-Down als Closures, voteUp_click und voteDown_click, implementiert sind, die Zugriff auf externe Variablen isVotedUp und isVotedDown haben, die global definiert sind. (Der Einfachheit halber beziehe ich mich auf die Frage-Abstimmungsschaltflächen von StackOverflow und nicht auf die Reihe der Antwort-Abstimmungsschaltflächen.)

Wenn der Benutzer auf die Schaltfläche VoteUp klickt, prüft die Funktion voteUp_click, ob isVotedDown == true ist, um zu bestimmen, ob eine Abstimmung nach oben oder nur nach unten abgebrochen werden soll. Die Funktion voteUp_click ist eine Schließung, weil sie auf ihre Umgebung zugreift.

var isVotedUp = false;
var isVotedDown = false;

function voteUp_click() {
  if (isVotedUp)
    return;
  else if (isVotedDown)
    SetDownVote(false);
  else
    SetUpVote(true);
}

function voteDown_click() {
  if (isVotedDown)
    return;
  else if (isVotedUp)
    SetUpVote(false);
  else
    SetDownVote(true);
}

function SetUpVote(status) {
  isVotedUp = status;
  // Do some CSS stuff to Vote-Up button
}

function SetDownVote(status) {
  isVotedDown = status;
  // Do some CSS stuff to Vote-Down button
}

Alle vier Funktionen sind Abschlüsse, da sie alle auf ihre Umgebung zugreifen.

77
John Pick

Der Autor von Closures hat Closures ziemlich gut erklärt, erklärt, warum wir sie brauchen und erklärt auch LexicalEnvironment, das zum Verständnis von Closures notwendig ist.
Hier ist die Zusammenfassung:

Was passiert, wenn auf eine Variable zugegriffen wird, diese jedoch nicht lokal ist? Wie hier:

Enter image description here

In diesem Fall findet der Interpreter die Variable im äußeren LexicalEnvironment Objekt.

Der Prozess besteht aus zwei Schritten:

  1. Erstens, wenn eine Funktion f erstellt wird, wird sie nicht in einem leeren Raum erstellt. Es gibt ein aktuelles LexicalEnvironment-Objekt. Im obigen Fall ist es das Fenster (a ist zum Zeitpunkt der Funktionserstellung undefiniert).

Enter image description here

Wenn eine Funktion erstellt wird, erhält sie eine versteckte Eigenschaft mit dem Namen [[Scope]], die auf die aktuelle LexicalEnvironment verweist.

Enter image description here

Wenn eine Variable gelesen wird, aber nirgendwo gefunden wird, wird ein Fehler generiert.

Verschachtelte Funktionen

Funktionen können ineinander verschachtelt werden und bilden eine Kette von LexicalEnvironments, die auch als Scope-Kette bezeichnet werden kann.

Enter image description here

Also hat die Funktion g Zugriff auf g, a und f.

Verschlüsse

Eine verschachtelte Funktion kann weiterhin ausgeführt werden, nachdem die äußere Funktion beendet wurde:

Enter image description here

LexicalEnvironments auszeichnen:

Enter image description here

Wie wir sehen, ist this.say eine Eigenschaft im Benutzerobjekt, so dass es auch nach Beendigung durch den Benutzer weiterlebt.

Und wenn Sie sich erinnern, dass beim Erstellen von this.say (wie bei jeder Funktion) ein interner Verweis this.say.[[Scope]] auf die aktuelle Lexikalische Umgebung erstellt wird. Die Lexikalische Umgebung der aktuellen Benutzerausführung bleibt also im Speicher. Alle Variablen des Benutzers sind auch seine Eigenschaften, so dass sie auch sorgfältig aufbewahrt werden und nicht wie gewöhnlich verschwendet werden.

Der springende Punkt ist, sicherzustellen, dass die innere Funktion, wenn sie in Zukunft auf eine äußere Variable zugreifen möchte, dies kann.

Zusammenfassen:

  1. Die innere Funktion behält einen Verweis auf die äußere Lexikalische Umgebung.
  2. Die innere Funktion kann jederzeit auf Variablen zugreifen, auch wenn die äußere Funktion beendet ist.
  3. Der Browser behält die LexicalEnvironment und alle ihre Eigenschaften (Variablen) im Speicher, bis es eine innere Funktion gibt, die auf sie verweist.

Dies nennt man einen Verschluss.

75
Arvand

Als Vater eines 6-jährigen Kindes, das gerade Kleinkinder unterrichtet (und ein relativ unerfahrener Programmierer ohne formale Ausbildung, sodass Korrekturen erforderlich sind), sollte die Lektion meiner Meinung nach am besten im praktischen Spiel bestehen bleiben. Wenn der 6-Jährige bereit ist zu verstehen, was eine Schließung ist, dann sind sie alt genug, um es selbst zu versuchen. Ich würde vorschlagen, den Code in jsfiddle.net einzufügen, ein wenig zu erklären und sie in Ruhe zu lassen, um ein einzigartiges Lied zu erfinden. Der erläuternde Text unten ist wahrscheinlich besser für einen 10-Jährigen geeignet.

function sing(person) {

    var firstPart = "There was " + person + " who swallowed ";

    var fly = function() {
        var creature = "a fly";
        var result = "Perhaps she'll die";
        alert(firstPart + creature + "\n" + result);
    };

    var spider = function() {
        var creature = "a spider";
        var result = "that wiggled and jiggled and tickled inside her";
        alert(firstPart + creature + "\n" + result);
    };

    var bird = function() {
        var creature = "a bird";
        var result = "How absurd!";
        alert(firstPart + creature + "\n" + result);
    };

    var cat = function() {
        var creature = "a cat";
        var result = "Imagine That!";
        alert(firstPart + creature + "\n" + result);
    };

    fly();
    spider();
    bird();
    cat();
}

var person="an old lady";

sing(person);

ANWEISUNGEN

DATA: Daten sind eine Sammlung von Fakten. Es können Zahlen, Wörter, Maße, Beobachtungen oder auch nur Beschreibungen von Dingen sein. Man kann es nicht anfassen, riechen oder schmecken. Sie können es aufschreiben, sprechen und hören. Sie können es verwenden, um mit einem Computer Geruch und Geschmack zu erzeugen . Es kann von einem Computer mithilfe von Code nützlich gemacht werden.

CODE: Alle oben genannten Schriftarten werden als Code bezeichnet. Es ist in JavaScript geschrieben.

JAVASCRIPT: JavaScript ist eine Sprache. Wie Englisch oder Französisch oder Chinesisch sind Sprachen. Es gibt viele Sprachen, die von Computern und anderen elektronischen Prozessoren verstanden werden. Damit JavaScript von einem Computer verstanden werden kann, ist ein Interpreter erforderlich. Stellen Sie sich vor, ein Lehrer, der nur Russisch spricht, kommt, um Ihre Klasse in der Schule zu unterrichten. Wenn der Lehrer "все садятся" sagt, würde die Klasse nicht verstehen. Aber zum Glück haben Sie einen russischen Schüler in Ihrer Klasse, der allen sagt, das bedeutet "alle setzen sich" - so tun Sie das alle. Die Klasse ist wie ein Computer und der russische Schüler ist der Dolmetscher. Für JavaScript wird der häufigste Interpreter als Browser bezeichnet.

BROWSER: Wenn Sie auf einem Computer, Tablet oder Telefon eine Verbindung zum Internet herstellen, um eine Website zu besuchen, verwenden Sie einen Browser. Beispiele, die Sie vielleicht kennen, sind Internet Explorer, Chrome, Firefox und Safari. Der Browser kann JavaScript verstehen und dem Computer mitteilen, was er tun muss. Die JavaScript-Anweisungen heißen Funktionen.

FUNKTION: Eine Funktion in JavaScript ist wie eine Fabrik. Es könnte sich um eine kleine Fabrik mit nur einer Maschine handeln. Oder es gibt viele andere kleine Fabriken mit jeweils vielen Maschinen, die unterschiedliche Arbeiten ausführen. In einer echten Kleiderfabrik kann es vorkommen, dass Unmengen von Stoffen und Garnspulen hereinkommen und T-Shirts und Jeans herauskommen. Unsere JavaScript-Fabrik verarbeitet nur Daten, sie kann keine Löcher nähen, bohren oder Metall schmelzen. In unserer JavaScript-Factory werden Daten ein- und ausgegeben.

All diese Daten klingen ein bisschen langweilig, aber es ist wirklich sehr cool. Wir könnten eine Funktion haben, die einem Roboter sagt, was er für das Abendessen machen soll. Nehmen wir an, ich lade Sie und Ihren Freund in mein Haus ein. Du magst Hähnchenschenkel am liebsten, ich mag Würstchen, dein Freund will immer was du willst und mein Freund isst kein Fleisch.

Ich habe keine Zeit zum Einkaufen, daher muss die Funktion wissen, was wir im Kühlschrank haben, um Entscheidungen zu treffen. Jede Zutat hat eine andere Garzeit und wir möchten, dass alles gleichzeitig vom Roboter heiß serviert wird. Wir müssen der Funktion die Daten über das zur Verfügung stellen, was uns gefällt, die Funktion könnte mit dem Kühlschrank "sprechen" und die Funktion könnte den Roboter steuern.

Eine Funktion hat normalerweise einen Namen, Klammern und Klammern. So was:

function cookMeal() {  /*  STUFF INSIDE THE FUNCTION  */  }

Beachten Sie, dass /*...*/ und // das Lesen des Codes durch den Browser stoppen.

NAME: Sie können eine Funktion für jedes gewünschte Wort aufrufen. Das Beispiel "cookMeal" ist typisch, wenn zwei Wörter zusammengefügt werden und das zweite am Anfang mit einem Großbuchstaben versehen wird - dies ist jedoch nicht erforderlich. Es kann kein Leerzeichen enthalten und es kann keine Zahl für sich sein.

KLAMMERN: "Klammern" oder () sind der Briefkasten an der Tür der JavaScript-Funktionsfabrik oder ein Briefkasten auf der Straße zum Senden von Informationspaketen an die Fabrik. Manchmal kann das Postfach mit markiert sein, z. B. cookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime). In diesem Fall wissen Sie, welche Daten Sie angeben müssen.

Hosenträger: "Hosenträger", die so aussehen {}, sind die getönten Scheiben unserer Fabrik. Von der Fabrik aus kann man raussehen, aber von außen kann man nicht hineinsehen.

DAS LANGE CODE-BEISPIEL OBEN

Unser Code beginnt mit der Funktion Word , wir wissen also, dass es eine ist! Dann ist der Name der Funktion sing - das ist meine eigene Beschreibung, worum es in der Funktion geht. Dann runde Klammern () . Die Klammern stehen immer für eine Funktion. Manchmal sind sie leer und manchmal haben sie etwas in. Dieser hat ein Wort in: (person). Danach folgt eine Klammer wie diese {. Dies markiert den Start der Funktion sing () . Es hat einen Partner, der das Ende von sing () so markiert }

function sing(person) {  /* STUFF INSIDE THE FUNCTION */  }

Diese Funktion könnte also etwas mit Singen zu tun haben und Daten über eine Person benötigen. Es enthält Anweisungen, um etwas mit diesen Daten zu tun.

Nun steht nach der Funktion sing () am Ende des Codes die Zeile

var person="an old lady";

VARIABLE: Die Buchstaben var stehen für "variabel". Eine Variable ist wie ein Umschlag. Auf der Außenseite ist dieser Umschlag mit "Person" gekennzeichnet. Auf der Innenseite befindet sich ein Zettel mit den Informationen, die unsere Funktion benötigt. Einige Buchstaben und Leerzeichen sind wie ein Stück Schnur (es wird als Schnur bezeichnet) miteinander verbunden. Unser Umschlag könnte andere Arten von Dingen enthalten, wie Zahlen (als Ganzzahlen bezeichnet), Anweisungen (als Funktionen bezeichnet), Listen (als Arrays ). Da diese Variable außerhalb aller geschweiften Klammern {} geschrieben ist und Sie durch die getönten Fenster sehen können, wenn Sie sich innerhalb der geschweiften Klammern befinden, kann diese Variable von einer beliebigen Stelle im Code aus gesehen werden. Wir nennen dies eine "globale Variable".

GLOBAL VARIABLE: person ist eine globale Variable. Wenn Sie den Wert von "eine alte Dame" in "ein junger Mann" ändern, wird die Variable person bleibt ein junger Mann, bis Sie sich entschließen, ihn erneut zu ändern und jede andere Funktion im Code erkennt, dass es sich um einen jungen Mann handelt. Drücken Sie die F12 Klicken Sie auf die Schaltfläche oder klicken Sie auf die Optionseinstellungen, um die Entwicklerkonsole eines Browsers zu öffnen, und geben Sie "person" ein, um den Wert zu ermitteln. Geben Sie person="a young man" ein, um es zu ändern, und geben Sie dann erneut "person" ein, um festzustellen, dass es geändert wurde.

Danach haben wir die Linie

sing(person);

Diese Zeile ruft die Funktion auf, als würde sie einen Hund aufrufen

"Komm sing , komm und hol die Person !"

Wenn der Browser den JavaScript-Code geladen hat und diese Zeile erreicht hat, startet er die Funktion. Ich setze die Zeile an das Ende, um sicherzustellen, dass der Browser über alle Informationen verfügt, die er zum Ausführen benötigt.

Funktionen definieren Aktionen - die Hauptfunktion ist das Singen. Es enthält eine Variable mit dem Namen firstPart , die für das Singen über die Person gilt, das für jeden der Verse des Liedes gilt: "Da war" + person + "wer schluckte". Wenn Sie firstPart in die Konsole eingeben, erhalten Sie keine Antwort, da die Variable in einer Funktion gesperrt ist - der Browser kann nicht hineinsehen die getönten Scheiben der Hosenträger.

CLOSURES: Die Closures sind die kleineren Funktionen, die sich innerhalb der großen sing () Funktion befinden. Die kleinen Fabriken in der großen Fabrik. Sie haben jeweils eigene Klammern, sodass die Variablen in ihnen von außen nicht sichtbar sind. Deshalb können die Namen der Variablen ( Kreatur und Ergebnis ) sein in den Verschlüssen aber mit unterschiedlichen Werten wiederholt. Wenn Sie diese Variablennamen in das Konsolenfenster eingeben, erhalten Sie den Wert nicht, da er durch zwei Schichten getönter Fenster verborgen wird.

Die Closures wissen alle, was die Variable der sing () -Funktion heißt firstPart , weil sie aus ihren getönten Scheiben sehen können.

Nach der Schließung kommen die Leitungen

fly();
spider();
bird();
cat();

Die Funktion sing () ruft jede dieser Funktionen in der angegebenen Reihenfolge auf. Dann wird die Arbeit der sing () - Funktion erledigt.

59
grateful

Okay, im Gespräch mit einem 6-jährigen Kind würde ich möglicherweise folgende Assoziationen verwenden.

Stellen Sie sich vor, Sie spielen im ganzen Haus mit Ihren kleinen Brüdern und Schwestern, bewegen sich mit Ihren Spielsachen herum und bringen einige davon in das Zimmer Ihres älteren Bruders. Nach einer Weile kehrte Ihr Bruder von der Schule zurück und ging in sein Zimmer, und er schloss es ein, so dass Sie jetzt nicht mehr direkt auf Spielzeug zugreifen konnten, das Sie dort gelassen hatten. Aber du könntest an die Tür klopfen und deinen Bruder nach diesem Spielzeug fragen. Dies nennt man toy's Closure ; Ihr Bruder hat es für Sie nachgeholt, und er befindet sich jetzt im äußeren Bereich .

Vergleichen Sie dies mit einer Situation, in der eine Tür durch Zugluft verriegelt wurde und niemand drinnen war (allgemeine Funktionsausführung) und dann ein örtliches Feuer auftrat und den Raum niederbrannte (Müllabfuhr: D), und dann ein neuer Raum gebaut wurde und Sie jetzt gehen können ein anderes Spielzeug gibt es (neue Funktionsinstanz), aber nie die gleichen Spielsachen bekommen, die in der ersten Rauminstanz zurückgelassen wurden.

Für ein fortgeschrittenes Kind würde ich folgendes sagen. Es ist nicht perfekt, aber man fühlt, was es ist:

function playingInBrothersRoom (withToys) {
  // We closure toys which we played in the brother's room. When he come back and lock the door
  // your brother is supposed to be into the outer [[scope]] object now. Thanks god you could communicate with him.
  var closureToys = withToys || [],
      returnToy, countIt, toy; // Just another closure helpers, for brother's inner use.

  var brotherGivesToyBack = function (toy) {
    // New request. There is not yet closureToys on brother's hand yet. Give him a time.
    returnToy = null;
    if (toy && closureToys.length > 0) { // If we ask for a specific toy, the brother is going to search for it.

      for ( countIt = closureToys.length; countIt; countIt--) {
        if (closureToys[countIt - 1] == toy) {
          returnToy = 'Take your ' + closureToys.splice(countIt - 1, 1) + ', little boy!';
          break;
        }
      }
      returnToy = returnToy || 'Hey, I could not find any ' + toy + ' here. Look for it in another room.';
    }
    else if (closureToys.length > 0) { // Otherwise, just give back everything he has in the room.
      returnToy = 'Behold! ' + closureToys.join(', ') + '.';
      closureToys = [];
    }
    else {
      returnToy = 'Hey, lil shrimp, I gave you everything!';
    }
    console.log(returnToy);
  }
  return brotherGivesToyBack;
}
// You are playing in the house, including the brother's room.
var toys = ['teddybear', 'car', 'jumpingrope'],
    askBrotherForClosuredToy = playingInBrothersRoom(toys);

// The door is locked, and the brother came from the school. You could not cheat and take it out directly.
console.log(askBrotherForClosuredToy.closureToys); // Undefined

// But you could ask your brother politely, to give it back.
askBrotherForClosuredToy('teddybear'); // Hooray, here it is, teddybear
askBrotherForClosuredToy('ball'); // The brother would not be able to find it.
askBrotherForClosuredToy(); // The brother gives you all the rest
askBrotherForClosuredToy(); // Nothing left in there

Wie Sie sehen, sind die im Raum zurückgelassenen Spielsachen immer noch über den Bruder zugänglich, unabhängig davon, ob der Raum verschlossen ist. Hier ist ein Jsbin um damit zu spielen.

56
dmi3y

Eine Antwort für einen Sechsjährigen (vorausgesetzt er weiß, was eine Funktion ist und was eine Variable ist und welche Daten sind):

Funktionen können Daten zurückgeben. Eine Art von Daten, die Sie von einer Funktion zurückgeben können, ist eine andere Funktion. Wenn diese neue Funktion zurückgegeben wird, verschwinden alle Variablen und Argumente, die in der Funktion verwendet wurden, die sie erstellt hat. Stattdessen wird diese übergeordnete Funktion "geschlossen". Mit anderen Worten, nichts kann darin nachsehen und die verwendeten Variablen sehen, außer der zurückgegebenen Funktion. Diese neue Funktion hat die besondere Fähigkeit, in die Funktion, die sie erstellt hat, zurückzublicken und die darin enthaltenen Daten anzuzeigen.

function the_closure() {
  var x = 4;
  return function () {
    return x; // Here, we look back inside the_closure for the value of x
  }
}

var myFn = the_closure();
myFn(); //=> 4

Eine andere wirklich einfache Art, es zu erklären, ist in Bezug auf den Umfang:

Jedes Mal, wenn Sie einen kleineren Bereich innerhalb eines größeren Bereichs erstellen, kann der kleinere Bereich immer sehen, was sich im größeren Bereich befindet.

50
Stupid Stupid

Eine Funktion in JavaScript ist nicht nur ein Verweis auf eine Reihe von Anweisungen (wie in der Sprache C), sondern enthält auch eine verborgene Datenstruktur, die sich aus Verweisen auf alle von ihr verwendeten nichtlokalen Variablen (erfassten Variablen) zusammensetzt. Solche zweiteiligen Funktionen werden Verschlüsse genannt. Jede Funktion in JavaScript kann als Abschluss betrachtet werden.

Abschlüsse sind Funktionen mit einem Zustand. Es ist "this" in dem Sinne ähnlich, dass "this" auch den Status für eine Funktion bereitstellt, aber function und "this" separate Objekte sind ("this" ist nur ein ausgefallener Parameter und die einzige Möglichkeit, ihn dauerhaft an a zu binden Funktion ist es, einen Abschluss zu erstellen). Während "this" und function immer getrennt voneinander leben, kann eine Funktion nicht von ihrem Abschluss getrennt werden, und die Sprache bietet keine Möglichkeit, auf erfasste Variablen zuzugreifen.

Da alle diese externen Variablen, auf die von einer lexikalisch verschachtelten Funktion verwiesen wird, tatsächlich lokale Variablen in der Kette ihrer lexikalisch einschließenden Funktionen sind (globale Variablen können als lokale Variablen einiger Stammfunktionen angesehen werden), werden bei jeder einzelnen Ausführung einer Funktion neue Instanzen von erstellt Aufgrund der lokalen Variablen wird bei jeder Ausführung einer Funktion, die eine verschachtelte Funktion zurückgibt (oder auf andere Weise überträgt, z. B. als Rückruf registriert), eine neue Closure erstellt (mit einem möglicherweise eindeutigen Satz referenzierter nicht lokaler Variablen, die ihre Ausführung darstellen) Kontext).

Es muss auch klar sein, dass lokale Variablen in JavaScript nicht auf dem Stack-Frame, sondern auf dem Heap erstellt und nur dann zerstört werden, wenn niemand auf sie verweist. Wenn eine Funktion zurückgegeben wird, werden Verweise auf ihre lokalen Variablen dekrementiert, sie können jedoch immer noch nicht null sein, wenn sie während der aktuellen Ausführung Teil eines Closures wurden und immer noch von ihren lexikalisch verschachtelten Funktionen referenziert werden (was nur passieren kann, wenn die Verweise auf Diese verschachtelten Funktionen wurden zurückgegeben oder anderweitig an einen externen Code übertragen.

Ein Beispiel:

function foo (initValue) {
   //This variable is not destroyed when the foo function exits.
   //It is 'captured' by the two nested functions returned below.
   var value = initValue;

   //Note that the two returned functions are created right now.
   //If the foo function is called again, it will return
   //new functions referencing a different 'value' variable.
   return {
       getValue: function () { return value; },
       setValue: function (newValue) { value = newValue; }
   }
}

function bar () {
    //foo sets its local variable 'value' to 5 and returns an object with
    //two functions still referencing that local variable
    var obj = foo(5);

    //Extracting functions just to show that no 'this' is involved here
    var getValue = obj.getValue;
    var setValue = obj.setValue;

    alert(getValue()); //Displays 5
    setValue(10);
    alert(getValue()); //Displays 10

    //At this point getValue and setValue functions are destroyed
    //(in reality they are destroyed at the next iteration of the garbage collector).
    //The local variable 'value' in the foo is no longer referenced by
    //anything and is destroyed too.
}

bar();
50
srgstm

Vielleicht ein bisschen jenseits von allem, außer dem frühreifsten der Sechsjährigen, aber ein paar Beispiele, die dazu beigetragen haben, dass das Konzept der Schließung in JavaScript für mich klickte.

Ein Closure ist eine Funktion, die Zugriff auf den Gültigkeitsbereich einer anderen Funktion (deren Variablen und Funktionen) hat. Der einfachste Weg, einen Abschluss zu erstellen, ist mit einer Funktion innerhalb einer Funktion. Der Grund dafür ist, dass eine Funktion in JavaScript immer Zugriff auf den Gültigkeitsbereich der enthaltenen Funktion hat.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    innerFunction();
}

outerFunction();

ALARM: Affe

Im obigen Beispiel wird outerFunction aufgerufen, was wiederum innerFunction aufruft. Beachten Sie, wie OuterVar für InnerFunction verfügbar ist. Dies wird durch die korrekte Warnung des Werts von OuterVar belegt.

Betrachten Sie nun Folgendes:

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

ALARM: Affe

referenceToInnerFunction wird auf outerFunction () gesetzt, wodurch einfach eine Referenz auf innerFunction zurückgegeben wird. Wenn referenceToInnerFunction aufgerufen wird, gibt es outerVar zurück. Wiederum wie oben zeigt dies, dass innerFunction Zugriff auf outerVar hat, eine Variable von outerFunction. Darüber hinaus ist es interessant festzustellen, dass dieser Zugriff auch nach Abschluss der Ausführung von outerFunction erhalten bleibt.

Und hier wird es wirklich interessant. Wenn wir OuterFunction loswerden würden, z. B. auf null setzen, könnten Sie denken, dass referenceToInnerFunction den Zugriff auf den Wert von OuterVar verlieren würde. Dies ist jedoch nicht der Fall.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

outerFunction = null;
alert(referenceToInnerFunction());

Warnung: Affe Warnung: Affe

Aber wie ist das so? Wie kann referenceToInnerFunction den Wert von outerVar jetzt noch kennen, wenn outerFunction auf null gesetzt wurde?

Der Grund, warum referenceToInnerFunction weiterhin auf den Wert von outerVar zugreifen kann, liegt darin, dass innerFunction beim erstmaligen Erstellen des Verschlusses durch Platzieren von innerFunction innerhalb von outerFunction einen Verweis auf den Gültigkeitsbereich von outerFunction (seine Variablen und Funktionen) zu seiner Gültigkeitskette hinzugefügt hat. Dies bedeutet, dass innerFunction einen Zeiger oder eine Referenz auf alle Variablen von outerFunction hat, einschließlich outerVar. Selbst wenn OuterFunction ausgeführt wurde oder wenn es gelöscht oder auf null gesetzt wurde, bleiben die Variablen in seinem Gültigkeitsbereich wie OuterVar im Speicher erhalten, da die zurückgegebene InnerFunction einen hervorragenden Bezug zu ihnen hat referenceToInnerFunction. Um OuterVar und die restlichen Variablen von OuterFunction wirklich aus dem Speicher zu entfernen, müssten Sie diesen hervorragenden Verweis auf sie entfernen, indem Sie beispielsweise referenceToInnerFunction ebenfalls auf null setzen.

////////////

Zwei andere Dinge über Verschlüsse zu beachten. Erstens hat der Abschluss immer Zugriff auf die letzten Werte seiner enthaltenden Funktion.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    outerVar = "gorilla";

    innerFunction();
}

outerFunction();

Warnung: Gorilla

Zweitens wird beim Erstellen eines Abschlusses ein Verweis auf alle Variablen und Funktionen der einschließenden Funktion beibehalten. es ist nicht möglich zu wählen. Und so sollten Verschlüsse sparsam oder zumindest vorsichtig verwendet werden, da sie speicherintensiv sein können. Viele Variablen können gespeichert werden, lange nachdem eine enthaltende Funktion ausgeführt wurde.

48

Ich würde sie einfach auf die Seite Mozilla Closures verweisen. Es ist die beste präzise und einfache Erklärung der Verschlussgrundlagen und der praktischen Anwendung, die ich gefunden habe. Es wird jedem empfohlen, der JavaScript lernt.

Und ja, ich würde es sogar einem 6-Jährigen empfehlen - wenn der 6-Jährige etwas über Verschlüsse lernt, ist es logisch, dass er bereit ist, die kurze und einfache Erklärung zu verstehen im Artikel angegeben.

46
mjmoody383

Ich glaube an kürzere Erklärungen, siehe das folgende Bild.

Enter image description here

function f1() ..> Hellrote Box

function f2() ..> Rote kleine Box

Hier haben wir zwei Funktionen, f1() und f2(). f2 () ist innerlich von f1 (). f1 () hat eine Variable, var x = 10.

Beim Aufrufen der Funktion f1() kann f2() auf den Wert von var x = 10 zugreifen.

Hier ist der Code:

function f1() {
    var x=10;

    function f2() {
        console.log(x)
    }

    return f2

}
f1()

f1() hier aufrufen:

Enter image description here

42
Dinesh Kanivu

Ein Abschluss ist eine Funktion, die Zugriff auf den übergeordneten Bereich hat, auch nachdem die übergeordnete Funktion geschlossen wurde.

Ein Verschluss ist also im Grunde eine Funktion einer anderen Funktion. Wir können sagen, wie eine Kinderfunktion.

Ein Closure ist eine innere Funktion, die Zugriff auf die Variablen der äußeren (einschließenden) Funktion hat - die Scope-Kette. Der Abschluss hat drei Gültigkeitsbereichsketten: Er hat Zugriff auf seinen eigenen Gültigkeitsbereich (Variablen, die in geschweiften Klammern angegeben sind), er hat Zugriff auf die Variablen der äußeren Funktion und er hat Zugriff auf die globalen Variablen.

Die innere Funktion hat nicht nur Zugriff auf die Variablen der äußeren Funktion, sondern auch auf die Parameter der äußeren Funktion. Beachten Sie, dass die innere Funktion das Argumentobjekt der äußeren Funktion nicht aufrufen kann, obwohl sie die Parameter der äußeren Funktion direkt aufrufen kann.

Sie erstellen einen Abschluss, indem Sie eine Funktion in eine andere Funktion einfügen.

Es ist auch eine sehr nützliche Methode, die in vielen bekannten Frameworks verwendet wird, einschließlich Angular, Node.js und jQuery:

Closures werden in Node.js häufig verwendet. Sie sind Arbeitspferde in Node.js asynchroner, nicht blockierender Architektur. Closures werden auch häufig in jQuery und in nahezu jedem von Ihnen gelesenen JavaScript-Code verwendet.

Aber wie sehen die Verschlüsse in einer echten Codierung aus? Schauen Sie sich diesen einfachen Beispielcode an:

function showName(firstName, lastName) {
      var nameIntro = "Your name is ";
      // this inner function has access to the outer function's variables, including the parameter
      function makeFullName() {
          return nameIntro + firstName + " " + lastName;
      }
      return makeFullName();
  }

  console.log(showName("Michael", "Jackson")); // Your name is Michael Jackson

Dies ist auch ein klassischer Abschlussweg in jQuery, den jeder Javascript- und jQuery-Entwickler häufig verwendet hat:

$(function() {
    var selections = [];
    $(".niners").click(function() { // this closure has access to the selections variable
        selections.Push(this.prop("name")); // update the selections variable in the outer function's scope
    });
});

Aber warum verwenden wir Verschlüsse? Wann verwenden wir es in einer tatsächlichen Programmierung? Was ist der praktische Nutzen von Verschlüssen? Das Folgende ist eine gute Erklärung und ein Beispiel von MDN:

Praktische Verschlüsse

Closures sind nützlich, weil Sie damit einige Daten (die lexikalische Umgebung) mit einer Funktion verknüpfen können, die diese Daten verarbeitet. Dies hat offensichtliche Parallelen zur objektorientierten Programmierung, bei der Objekte es uns ermöglichen, einige Daten (die Objekteigenschaften) einer oder mehreren Methoden zuzuordnen.

Folglich können Sie einen Abschluss überall dort verwenden, wo Sie normalerweise ein Objekt mit nur einer Methode verwenden.

Situationen, in denen Sie dies tun möchten, sind im Web besonders häufig. Ein Großteil des Codes, den wir in Front-End-JavaScript schreiben, ist ereignisbasiert. Wir definieren ein bestimmtes Verhalten und hängen es dann an ein vom Benutzer ausgelöstes Ereignis an (z. B. einen Klick oder einen Tastendruck). Unser Code wird im Allgemeinen als Rückruf angehängt: eine einzelne Funktion, die als Reaktion auf das Ereignis ausgeführt wird.

Angenommen, wir möchten einer Seite einige Schaltflächen hinzufügen, mit denen die Textgröße angepasst werden kann. Eine Möglichkeit, dies zu tun, besteht darin, die Schriftgröße des body-Elements in Pixel anzugeben und dann die Größe der anderen Elemente auf der Seite (z. B. Überschriften) mithilfe der relativen em-Einheit festzulegen:

Lesen Sie den folgenden Code und führen Sie den Code aus, um zu sehen, wie uns das Schließen hier dabei hilft, separate Funktionen für jeden Abschnitt zu erstellen:

//javascript
function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
/*css*/
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}
<!--html><!-->
<p>Some paragraph text</p>
<h1>some heading 1 text</h1>
<h2>some heading 2 text</h2>

<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>

Für weitere Studien zu Schließungen empfehle ich Ihnen, diese Seite von MDN zu besuchen: https://developer.mozilla.org/en/docs/Web/JavaScript/Closures

34
Alireza

Für einen Sechsjährigen?

Sie und Ihre Familie leben in der mythischen Stadt Ann Ville. Sie haben einen Freund, der nebenan wohnt, also rufen Sie ihn an und bitten ihn, herauszukommen und zu spielen. Sie wählen:

000001 (jamiesHouse)

Nach einem Monat ziehen Sie und Ihre Familie aus Ann Ville in die nächste Stadt, aber Sie und Ihr Freund bleiben in Kontakt. Jetzt müssen Sie die Vorwahl der Stadt wählen, in der Ihr Freund lebt, bevor Sie die Nummer wählen. richtige 'Nummer:

001 000001 (annVille.jamiesHouse)

Ein Jahr später ziehen Ihre Eltern in ein ganz neues Land, aber Sie und Ihr Freund bleiben in Kontakt. Nachdem Sie Ihre Eltern abgehört haben, internationale Tarifanrufe zu tätigen, wählen Sie jetzt:

01 001 000001 (myOldCountry.annVille.jamiesHouse)

Seltsamerweise ziehen Sie und Ihre Familie nach Ihrem Umzug in Ihr neues Land zufällig in eine neue Stadt namens Ann Ville ... und Sie schließen zufällig Freundschaften mit einer neuen Person namens Jamie ... Sie schenken ihnen eine Anruf...

000001 (jamiesHouse)

Gruslig...

In der Tat so gruselig, dass du Jamie aus deinem alten Land davon erzählst ... Du hast ein gutes Lachen darüber. Eines Tages machen Sie und Ihre Familie Urlaub in der alten Heimat. Sie besuchen Ihre Altstadt (Ann Ville) und besuchen Jamie ...

  • "Wirklich? Noch ein Jamie? In Ann Ville? In deinem neuen Land !!?"
  • "Ja ... Nennen wir sie ..."

02 001 000001 (myNewCountry.annVille.jamiesHouse)

Meinungen?

Außerdem habe ich eine Menge Fragen zur Geduld eines modernen Sechsjährigen ...

30
Charlie

In JavaScript sind Closures fantastisch, bei denen Variablen oder Argumente für innere Funktionen verfügbar sind und sie auch nach der Rückkehr der äußeren Funktion lebendig bleiben.

  function getFullName(a, b) {
  return a + b;
}

function makeFullName(fn) {

  return function(firstName) {

    return function(secondName) {

      return fn(firstName, secondName);
    }
  }
}

makeFullName(getFullName)("stack")("overflow"); // Stackoverflow
29

Hier ist ein einfaches Echtzeitszenario. Lesen Sie es einfach durch und Sie werden verstehen, wie wir den Verschluss hier verwendet haben (sehen Sie, wie sich die Sitznummer ändert).

Alle anderen zuvor erläuterten Beispiele sind ebenfalls sehr gut, um das Konzept zu verstehen.

function movieBooking(movieName) {
    var bookedSeatCount = 0;
    return function(name) {
        ++bookedSeatCount ;
        alert( name + " - " + movieName + ", Seat - " + bookedSeatCount )
    };
};

var MI1 = movieBooking("Mission Impossible 1 ");
var MI2 = movieBooking("Mission Impossible 2 ");

MI1("Mayur");
// alert
// Mayur - Mission Impossible 1, Seat - 1

MI1("Raju");
// alert
// Raju - Mission Impossible 1, Seat - 2

MI2("Priyanka");
// alert
// Raja - Mission Impossible 2, Seat - 1
28
Mayur Randive

Mit Closures können JavaScript-Programmierer besseren Code schreiben. Kreativ, ausdrucksstark und prägnant. Wir verwenden häufig Closures in JavaScript, und unabhängig von unserer JavaScript-Erfahrung begegnen wir ihnen zweifellos immer wieder. Abschlüsse mögen komplex erscheinen, aber hoffentlich sind Abschlüsse nach dem Lesen viel einfacher zu verstehen und daher für Ihre alltäglichen JavaScript-Programmieraufgaben ansprechender.

Sie sollten mit JavaScript-Variablenbereich vertraut sein, bevor Sie da weiterlesen Um Abschlüsse zu verstehen, müssen Sie den variablen Bereich von JavaScript verstehen.

Was ist eine Schließung?

Ein Closure ist eine innere Funktion, die Zugriff auf die Variablen der äußeren (einschließenden) Funktion hat - die Scope-Kette. Der Abschluss hat drei Gültigkeitsbereichsketten: Er hat Zugriff auf seinen eigenen Gültigkeitsbereich (Variablen, die in geschweiften Klammern angegeben sind), er hat Zugriff auf die Variablen der äußeren Funktion und er hat Zugriff auf die globalen Variablen.

Die innere Funktion hat nicht nur Zugriff auf die Variablen der äußeren Funktion, sondern auch auf die Parameter der äußeren Funktion. Beachten Sie, dass die innere Funktion das Argumentobjekt der äußeren Funktion nicht aufrufen kann, obwohl sie die Parameter der äußeren Funktion direkt aufrufen kann.

Sie erstellen einen Abschluss, indem Sie eine Funktion in eine andere Funktion einfügen.

Ein grundlegendes Beispiel für Closures in JavaScript:

function showName (firstName, lastName) {

  var nameIntro = "Your name is ";
  // this inner function has access to the outer function's variables, including the parameter
  ​function makeFullName () {
            
​    return nameIntro + firstName + " " + lastName;
        
  }
​
​  return makeFullName ();

}

​
showName ("Michael", "Jackson"); // Your name is Michael Jackson


Closures werden in Node.js häufig verwendet. Sie sind Arbeitspferde in Node.js asynchroner, nicht blockierender Architektur. Closures werden auch häufig in jQuery und in nahezu jedem von Ihnen gelesenen JavaScript-Code verwendet.

Ein klassisches jQuery-Beispiel für Abschlüsse:

$(function() {
​
​  var selections = []; 
  $(".niners").click(function() { // this closure has access to the selections variable​
    selections.Push (this.prop("name")); // update the selections variable in the outer function's scope​
  });
​});

Verschlussregeln und Nebenwirkungen

1. Closures haben Zugriff auf die Variable der äußeren Funktion, auch wenn die äußere Funktion zurückgibt:

Eine der wichtigsten und heikelsten Eigenschaften von Closures ist, dass die innere Funktion auch nach der Rückkehr der äußeren Funktion noch Zugriff auf die Variablen der äußeren Funktion hat. Ja, das hast du richtig gelesen. Wenn Funktionen in JavaScript ausgeführt werden, verwenden sie dieselbe Bereichskette, die bei ihrer Erstellung wirksam war. Dies bedeutet, dass die innere Funktion auch nach der Rückkehr der äußeren Funktion weiterhin Zugriff auf die Variablen der äußeren Funktion hat. Daher können Sie die innere Funktion später in Ihrem Programm aufrufen. Dieses Beispiel zeigt:

function celebrityName (firstName) {
    var nameIntro = "This celebrity is ";
    // this inner function has access to the outer function's variables, including the parameter​
   function lastName (theLastName) {
        return nameIntro + firstName + " " + theLastName;
    }
    return lastName;
}
​
​var mjName = celebrityName ("Michael"); // At this juncture, the celebrityName outer function has returned.​
​
​// The closure (lastName) is called here after the outer function has returned above​
​// Yet, the closure still has access to the outer function's variables and parameter​
mjName ("Jackson"); // This celebrity is Michael Jackson


2. Closures speichern Verweise auf die Variablen der äußeren Funktion:

Sie speichern nicht den tatsächlichen Wert. Closures werden interessanter, wenn sich der Wert der Variablen der äußeren Funktion ändert, bevor das Closure aufgerufen wird. Und diese leistungsstarke Funktion kann auf kreative Weise genutzt werden, wie zum Beispiel dieses Beispiel für private Variablen, das zuerst von Douglas Crockford demonstriert wurde:

function celebrityID () {
    var celebrityID = 999;
    // We are returning an object with some inner functions​
    // All the inner functions have access to the outer function's variables​
    return {
        getID: function ()  {
            // This inner function will return the UPDATED celebrityID variable​
            // It will return the current value of celebrityID, even after the changeTheID function changes it​
          return celebrityID;
        },
        setID: function (theNewID)  {
            // This inner function will change the outer function's variable anytime​
            celebrityID = theNewID;
        }
    }
​
}
​
​var mjID = celebrityID (); // At this juncture, the celebrityID outer function has returned.​
mjID.getID(); // 999​
mjID.setID(567); // Changes the outer function's variable​
mjID.getID(); // 567: It returns the updated celebrityId variable


3. Verschlüsse schief gegangen

Da Closures Zugriff auf die aktualisierten Werte der Variablen der äußeren Funktion haben, können sie auch zu Fehlern führen, wenn sich die Variable der äußeren Funktion mit einer for-Schleife ändert. Somit:

// This example is explained in detail below (just after this code box).​
​function celebrityIDCreator (theCelebrities) {
    var i;
    var uniqueID = 100;
    for (i = 0; i < theCelebrities.length; i++) {
      theCelebrities[i]["id"] = function ()  {
        return uniqueID + i;
      }
    }
    
    return theCelebrities;
}
​
​var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
​
​var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
​
​var stalloneID = createIdForActionCelebs [0];

    console.log(stalloneID.id()); // 103


Mehr finden Sie hier-

  1. http://javascript.info/tutorial/closures

  2. http://www.javascriptkit.com/javatutors/closures.shtml

26
Abrar Jahin

Hier ist die Zen-Antwort, die ich geben kann:

Was würden Sie von diesem Code erwarten? Sag es mir in einem Kommentar, bevor du es ausführst. Ich bin neugierig!

function foo() {
  var i = 1;
  return function() {
    console.log(i++);
  }
}

var bar = foo();
bar();
bar();
bar();

var baz = foo();
baz();
baz();
baz();

Öffnen Sie nun die Konsole in Ihrem Browser (Ctrl + Shift + I oder F12, hoffentlich) und fügen Sie den Code ein und drücken Sie Enter.

Wenn dieser Code das druckt, was Sie erwarten (JavaScript-Neulinge - ignorieren Sie das "undefinierte" am Ende), dann haben Sie bereits wortloses Verständnis. In Worten, die Variable i ist Teil der inneren Funktion Instanzen Closure.

Ich habe es so ausgedrückt, weil mich nichts anderes überrascht hat, als ich begriffen habe, dass dieser Code Instanzen der inneren Funktion von foo() in bar und baz setzt und sie dann über diese Variablen aufruft.

Aber wenn ich falsch liege und die Konsolenausgabe Sie überrascht hat, lassen Sie es mich wissen!

24
Andy

Je mehr ich über das Schließen nachdenke, desto mehr sehe ich es als einen zweistufigen Prozess: init - action

init: pass first what's needed...
action: in order to achieve something for later execution.

Für einen 6-Jährigen würde ich den praktischen Aspekt des Abschlusses hervorheben:

Daddy: Listen. Could you bring mum some milk (2).
Tom: No problem.
Daddy: Take a look at the map that Daddy has just made: mum is there and daddy is here.
Daddy: But get ready first. And bring the map with you (1), it may come in handy
Daddy: Then off you go (3). Ok?
Tom: A piece of cake!

Beispiel : Bringen Sie etwas Milch zur Mutter (= Aktion). Machen Sie sich erst fertig und bringen Sie die Karte mit (= init).

function getReady(map) {
    var cleverBoy = 'I examine the ' + map;
    return function(what, who) {
        return 'I bring ' + what + ' to ' + who + 'because + ' cleverBoy; //I can access the map
    }
}
var offYouGo = getReady('daddy-map');
offYouGo('milk', 'mum');

Denn wenn Sie eine sehr wichtige Information (die Karte) mitbringen, sind Sie so gut informiert, dass Sie andere ähnliche Aktionen ausführen können:

offYouGo('potatoes', 'great mum');

Für einen Entwickler würde ich eine Parallele zwischen Closures und OOP machen. Die Init-Phase ähnelt der Übergabe von Argumenten an einen Konstruktor in einer traditionellen OO Sprache. Die Aktionsphase ist letztendlich die Methode, die Sie aufrufen, um das zu erreichen, was Sie wollen. Und die Methode hat Zugriff auf diese Init-Argumente mithilfe eines Mechanismus namens closure .

Siehe meine andere Antwort, die die Parallelität zwischen OO und Abschlüssen veranschaulicht:

Wie "richtig" ein benutzerdefiniertes Objekt in JavaScript erstellen?

22
roland

Gegeben die folgende Funktion

function person(name, age){

    var name = name;
    var age = age;

    function introduce(){
        alert("My name is "+name+", and I'm "+age);
    }

    return introduce;
}

var a = person("Jack",12);
var b = person("Matt",14);

Jedes Mal, wenn die Funktion person aufgerufen wird, wird ein neuer Abschluss erstellt. Während die Variablen a und b dieselbe Funktion introduce haben, ist sie mit verschiedenen Closures verknüpft. Und dieser Abschluss bleibt bestehen, auch nachdem die Funktion person die Ausführung beendet hat.

Enter image description here

a(); //My name is Jack, and I'm 12
b(); //My name is Matt, and I'm 14

Ein abstrakter Abschluss könnte folgendermaßen dargestellt werden:

closure a = {
    name: "Jack",
    age: 12,
    call: function introduce(){
        alert("My name is "+name+", and I'm "+age);
    }
}

closure b = {
    name: "Matt",
    age: 14,
    call: function introduce(){
        alert("My name is "+name+", and I'm "+age);
    }
}

Angenommen, Sie wissen, wie ein class in einer anderen Sprache funktioniert, dann werde ich eine Analogie ziehen.

Denke wie

  • JavaScript function als constructor
  • local variables als instance properties
  • diese properties sind privat
  • inner functions als instance methods

Jedes Mal, wenn ein function aufgerufen wird

  • Ein neues object mit allen lokalen Variablen wird erstellt.
  • Methoden dieses Objekts haben Zugriff auf "properties" dieses Instanzobjekts.
22
Vitim.us

Obwohl es im Internet viele schöne Definitionen von JavaScript-Verschlüssen gibt, versuche ich, meinen sechsjährigen Freund mit meinen bevorzugten Definitionen von Verschlüssen zu erklären, die mir geholfen haben, die Verschlüsse besser zu verstehen.

Was ist eine Schließung?

Ein Closure ist eine innere Funktion, die Zugriff auf die Variablen der äußeren (einschließenden) Funktion hat - die Scope-Kette. Der Abschluss hat drei Gültigkeitsbereichsketten: Er hat Zugriff auf seinen eigenen Gültigkeitsbereich (Variablen, die in geschweiften Klammern angegeben sind), er hat Zugriff auf die Variablen der äußeren Funktion und er hat Zugriff auf die globalen Variablen.

Ein Closure ist die lokale Variable für eine Funktion, die nach der Rückkehr der Funktion erhalten bleibt.

Closures sind Funktionen, die sich auf unabhängige (freie) Variablen beziehen. Mit anderen Worten, die im Abschluss definierte Funktion „merkt“ sich die Umgebung, in der sie erstellt wurde.

Verschlüsse sind eine Erweiterung des Geltungsbereichs. Mit Abschlüssen haben Funktionen Zugriff auf Variablen, die in dem Bereich verfügbar waren, in dem die Funktion erstellt wurde.

Ein Closure ist ein Stack-Frame, dessen Zuordnung nicht aufgehoben wird, wenn die Funktion zurückkehrt. (Als ob ein "Stapelrahmen" malloced wäre, anstatt auf dem Stapel zu sein!)

Sprachen wie Java bieten die Möglichkeit, Methoden als privat zu deklarieren. Dies bedeutet, dass sie nur von anderen Methoden in derselben Klasse aufgerufen werden können. JavaScript bietet keine native Möglichkeit, dies zu tun, aber es ist möglich, private Methoden mithilfe von Closures zu emulieren.

Ein "Abschluß" ist ein Ausdruck (normalerweise eine Funktion), der freie Variablen zusammen mit einer Umgebung haben kann, die diese Variablen bindet (die den Ausdruck "abschließt").

Closures sind ein Abstraktionsmechanismus, mit dem Sie Probleme sehr sauber trennen können.

Verwendung von Verschlüssen:

Closures sind nützlich, um die Implementierung von Funktionen zu verbergen und gleichzeitig die Benutzeroberfläche sichtbar zu machen.

Sie können das Kapselungskonzept in JavaScript mithilfe von Closures emulieren.

Closures werden häufig in jQuery und Node.js verwendet.

Während Objektliterale mit Sicherheit einfach zu erstellen und bequem zum Speichern von Daten sind, sind Verschlüsse häufig die bessere Wahl, um statische Singleton-Namespaces in einer großen Webanwendung zu erstellen.

Beispiel für Verschlüsse:

Angenommen, mein 6-jähriger Freund lernte Addition erst kürzlich in seiner Grundschule kennen. Ich war der Meinung, dass dieses Beispiel für das Addieren der beiden Zahlen für den 6-Jährigen das einfachste und geeignetste wäre, um die Schließung zu lernen.

Beispiel 1: Der Abschluss wird hier durch Rückgabe einer Funktion erreicht.

function makeAdder(x) {
    return function(y) {
        return x + y;
    };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

Beispiel 2: Der Abschluss wird hier durch die Rückgabe eines Objektliteral erreicht.

function makeAdder(x) {
    return {
        add: function(y){
            return x + y;
        }
    }
}

var add5 = makeAdder(5);
console.log(add5.add(2));//7

var add10 = makeAdder(10);
console.log(add10.add(2));//12

Beispiel 3: Closures in jQuery

$(function(){
    var name="Closure is easy";
    $('div').click(function(){
        $('p').text(name);
    });
});

Nützliche Links:

Dank der obigen Links kann ich den Abschluss besser verstehen und erklären.

21
Ravi

Um die Abschlüsse zu verstehen, müssen Sie das Programm aufrufen und buchstäblich so ausführen, als ob Sie die Laufzeit wären. Schauen wir uns diesen einfachen Code an:

Enter image description here

JavaScript führt den Code in zwei Phasen aus:

  • Kompilierungsphase // JavaScript ist keine rein interpretierte Sprache
  • Ausführungsphase

Wenn JavaScript die Kompilierungsphase durchläuft, werden die Deklarationen von Variablen und Funktionen extrahiert. Dies nennt man Heben. Funktionen, die in dieser Phase auftreten, werden als Textblobs im Speicher gespeichert, die auch als Lambda bezeichnet werden. Nach dem Kompilieren tritt JavaScript in die Ausführungsphase ein, in der es alle Werte zuweist und die Funktion ausführt. Zum Ausführen der Funktion wird der Ausführungskontext vorbereitet, indem Speicher aus dem Heap zugewiesen und die Kompilierungs- und Ausführungsphase für die Funktion wiederholt wird. Dieser Speicherbereich wird als Funktionsumfang bezeichnet. Es gibt einen globalen Bereich, in dem die Ausführung beginnt. Bereiche sind der Schlüssel zum Verständnis von Verschlüssen.

In diesem Beispiel wird zunächst die Variable a und dann f in der Kompilierungsphase definiert. Alle nicht deklarierten Variablen werden im globalen Bereich gespeichert. In der Ausführungsphase wird f mit einem Argument aufgerufen. Der Gültigkeitsbereich von f wird zugewiesen, und die Kompilierungs- und Ausführungsphase wird dafür wiederholt.

In diesem lokalen Bereich werden auch Argumente für f gespeichert. Immer wenn ein lokaler Ausführungskontext oder -bereich erstellt wird, enthält dieser einen Referenzzeiger auf den übergeordneten Bereich. Der gesamte variable Zugriff folgt dieser lexikalischen Bereichskette, um ihren Wert zu ermitteln. Wenn eine Variable im lokalen Bereich nicht gefunden wird, folgt sie der Kette und findet sie im übergeordneten Bereich. Dies ist auch der Grund, warum eine lokale Variable Variablen im übergeordneten Bereich überschreibt. Der übergeordnete Bereich wird als "Abschluss" für einen lokalen Bereich oder eine Funktion bezeichnet.

Hier wurde beim Einrichten des Bereichs von g ein lexikalischer Zeiger auf den übergeordneten Bereich von f erstellt. Der Geltungsbereich von f ist der Abschluss für g. Wenn in JavaScript auf Funktionen, Objekte oder Bereiche verwiesen wird und Sie diese irgendwie erreichen können, wird kein Müll gesammelt. Wenn myG ausgeführt wird, hat es einen Zeiger auf den Gültigkeitsbereich von f, der sein Abschluss ist. In diesem Speicherbereich wird kein Müll gesammelt, selbst wenn f zurückgegeben wurde. Dies ist ein Abschluß für die Laufzeit.

SO WAS IS EIN VERSCHLUSS?

  • Es ist ein implizites, permanentes Bindeglied zwischen einer Funktion und ihrer Scope-Kette ...
  • Die versteckte [[scope]] Referenz einer Funktionsdefinition (Lambda).
  • Hält die Scope-Kette (verhindert Garbage Collection).
  • Sie wird bei jeder Ausführung der Funktion als "Referenz für die äußere Umgebung" verwendet und kopiert.

STILLSCHWEIGENDE SCHLIESSUNG

var data = "My Data!";
setTimeout(function() {
  console.log(data); // Prints "My Data!"
}, 3000);

EXPLICIT CLOSURES

function makeAdder(n) {
  var inc = n;
  var sum = 0;
  return function add() {
    sum = sum + inc;
    return sum;
  };
}

var adder3 = makeAdder(3);

Ein sehr interessanter Vortrag über Abschlüsse und mehr ist Arindam Paul - JavaScript VM Interna, EventLoop, Async und ScopeChains.

21
Nikhil Ranjan

Ein Closure ist eine Funktion innerhalb einer Funktion , die Zugriff auf die Variablen und Parameter der übergeordneten Funktion hat .

Beispiel:

function showPostCard(Sender, Receiver) {

    var PostCardMessage = " Happy Spring!!! Love, ";

    function PreparePostCard() {
        return "Dear " + Receiver + PostCardMessage + Sender;
    }

    return PreparePostCard();
}
showPostCard("Granny", "Olivia");
21
enb081

Lernen Sie die illustrierte Erklärung kennen : Wie funktionieren JavaScript-Verschlüsse hinter den Kulissen? .

In diesem Artikel wird erläutert, wie die Bereichsobjekte (oder LexicalEnvironments) auf intuitive Weise zugeordnet und verwendet werden. Zum Beispiel für dieses einfache Skript:

"use strict";

var foo = 1;
var bar = 2;

function myFunc() {
  //-- Define local-to-function variables
  var a = 1;
  var b = 2;
  var foo = 3;
}

//-- And then, call it:
myFunc();

Bei der Ausführung des Codes der obersten Ebene haben wir die folgende Anordnung von Bereichsobjekten:

Enter image description here

Und wenn myFunc() aufgerufen wird, haben wir die folgende Bereichskette:

Enter image description here

Das Verständnis darüber, wie Scope-Objekte erstellt, verwendet und gelöscht werden, ist ein Schlüssel zum Überblick und zum Verständnis der Funktionsweise von Verschlüssen unter der Haube.

Siehe den oben genannten Artikel für alle Details.

21
Dmitry Frank

Versionsbild für diese Antwort: [Gelöst]

Vergessen Sie einfach alles über den Umfang und denken Sie daran: Wenn eine Variable irgendwo benötigt wird, zerstört Javascript sie nicht. Die Variable zeigt immer auf den neuesten Wert.

Beispiel 1:

enter image description here

Beispiel 2:

enter image description here

Beispiel 3: enter image description here

20

(Ich berücksichtige das 6-jährige Ding nicht.)

In einer Sprache wie JavaScript, in der Sie Funktionen als Parameter an andere Funktionen übergeben können (Sprachen, in denen Funktionen erstklassige Bürger sind ), tun Sie häufig Folgendes:

var name = 'Rafael';

var sayName = function() {
  console.log(name);
};

Sie sehen, sayName hat nicht die Definition für die Variable name, verwendet aber den Wert von name, der außerhalb von sayName (in einem übergeordneten Bereich) definiert wurde ).

Angenommen, Sie übergeben sayName als Parameter an eine andere Funktion, die sayName als Rückruf aufruft:

functionThatTakesACallback(sayName);

Beachten Sie, dass:

  1. sayName wird von innen functionThatTakesACallback aufgerufen (nehmen wir an, dass ich in diesem Beispiel functionThatTakesACallback nicht implementiert habe).
  2. Wenn sayName aufgerufen wird, wird der Wert der Variablen name protokolliert.
  3. functionThatTakesACallback definiert keine name -Variable (na ja, es könnte, aber es wäre egal, also nehmen wir an, dass es nicht so ist).

Wir haben also sayName, das in functionThatTakesACallback aufgerufen wird und auf eine name -Variable verweist, die in functionThatTakesACallback nicht definiert ist.

Was passiert dann? Ein ReferenceError: name is not defined?

Nein! Der Wert von name wird in einem Closure erfasst. Sie können sich diesen Abschluss als Kontext, der einer Funktion zugeordnet ist vorstellen, der die Werte enthält, die dort verfügbar waren, wo diese Funktion definiert wurde.

Also: Auch wenn name nicht in dem Bereich liegt, in dem die Funktion sayName aufgerufen wird (innerhalb von functionThatTakesACallback), kann sayName auf den Wert für name zugreifen erfasst in dem Verschluss, der mit sayName verknüpft ist.

-

Aus dem Buch Eloquentes JavaScript :

Ein gutes mentales Modell besteht darin, sich Funktionswerte vorzustellen, die sowohl den Code in ihrem Körper als auch die Umgebung enthalten, in der sie erstellt werden. Beim Aufruf sieht der Funktionskörper seine ursprüngliche Umgebung, nicht die Umgebung, in der der Aufruf erfolgt.

20
Rafael Eyng

Diese Antwort ist eine Zusammenfassung dieses YouTube-Videos Javascript Closures . Also volle Credits für das Video.

Closures sind nichts anderes als Stateful-Funktionen, die Zustände ihrer privaten Variablen beibehalten.

Normalerweise, wenn Sie eine Funktion aufrufen, wie in der folgenden Abbildung gezeigt. Die Variablen werden auf einem Stapel (mit RAM Arbeitsspeicher) erstellt und dann aufgehoben.

enter image description here

Aber jetzt gibt es Situationen, in denen wir diesen Zustand der Funktion beibehalten wollen, in der Javascript-Verschlüsse verwendet werden. Ein Closure ist eine Funktion innerhalb einer Funktion mit einem Rückruf, wie im folgenden Code gezeigt.

enter image description here

Daher sieht der Abschlusscode für die obige Zählerfunktion wie folgt aus. Er enthält eine Funktion in der Funktion mit einer return-Anweisung.

function Counter() {
           var counter = 0;

           var Increment = function () {
               counter++;
               alert(counter);
           }
           return {
               Increment
           }
       }

Wenn Sie also einen Anruf tätigen, erhöht sich der Zähler mit anderen Worten, der Funktionsaufruf behält den Status bei.

var x = Counter(); // get the reference of the closure
x.Increment(); // Displays 1
x.Increment(); // Display 2 ( Maintains the private variables)

Aber jetzt ist die größte Frage, was die Verwendung einer solchen Zustandsfunktion ist. Stateful-Funktionen sind Bausteine ​​zur Implementierung von OOP -Konzepten wie Abstraktion, Kapselung und Erstellung eigenständiger Module.

Was auch immer Sie möchten, Sie können es als privat kennzeichnen, und Dinge, die der Öffentlichkeit zugänglich gemacht werden sollen, sollten in die return-Anweisung aufgenommen werden. Außerdem sind diese Komponenten in sich abgeschlossene isolierte Objekte, sodass sie globale Variablen nicht verschmutzen.

Ein Objekt, das OOP Prinzipien folgt, ist in sich geschlossen, folgt der Abstraktion, folgt der Einkapselung und so. Ohne Schließungen in Javascript ist dies schwierig zu implementieren.

enter image description here

18

Aus einem persönlichen Blogpost :

Standardmäßig kennt JavaScript zwei Arten von Bereichen: globale und lokale.

var a = 1;

function b(x) {
    var c = 2;
    return x * c;
}

Im obigen Code sind die Variablen a und die Funktion b an einer beliebigen Stelle im Code verfügbar (dh global). Die Variable c ist nur im Funktionsbereich b (also lokal) verfügbar. Die meisten Softwareentwickler werden mit dieser mangelnden Flexibilität des Anwendungsbereichs nicht zufrieden sein, insbesondere bei großen Programmen.

JavaScript-Schließungen lösen dieses Problem, indem sie eine Funktion mit einem Kontext verknüpfen:

function a(x) {
    return function b(y) {
        return x + y;
    }
}

Hier gibt die Funktion a eine Funktion mit dem Namen b zurück. Da b in a definiert ist, hat es automatisch Zugriff auf alles, was in a definiert ist, in diesem Beispiel also auf x. Aus diesem Grund kann bx + y zurückgeben, ohne x zu deklarieren.

var c = a(3);

Die Variable c erhält das Ergebnis eines Aufrufs von a mit Parameter 3. Das heißt, eine Instanz der Funktion b mit x = 3. Mit anderen Worten, c ist jetzt eine Funktion, die äquivalent zu:

var c = function b(y) {
    return 3 + y;
}

Die Funktion b merkt sich, dass in ihrem Kontext x = 3 ist. Deshalb:

var d = c(4);

weist d den Wert 3 + 4 zu, dh 7.

Bemerkung : Wenn jemand den Wert von x ändert (sagen wir x = 22), nachdem die Instanz der Funktion b erstellt wurde, wird dies auch in b wiedergegeben . Daher würde ein späterer Aufruf von c (4) 22 + 4, also 26, zurückgeben.

Closures können auch verwendet werden, um den Umfang global deklarierter Variablen und Methoden einzuschränken:

(function () {
    var f = "Some message";
    alert(f);
})();

Das Obige ist ein Abschluß, bei dem die Funktion keinen Namen und kein Argument hat und sofort aufgerufen wird. Der hervorgehobene Code, der eine globale Variable f deklariert, beschränkt den Gültigkeitsbereich von f auf den Abschluss.

Jetzt gibt es eine allgemeine JavaScript-Einschränkung, bei der Verschlüsse helfen können:

var a = new Array();

for (var i=0; i<2; i++) {
    a[i]= function(x) { return x + i ; }
}

Aus den oben genannten Gründen würden die meisten davon ausgehen, dass das Array a wie folgt initialisiert wird:

a[0] = function (x) { return x + 0 ; }
a[1] = function (x) { return x + 1 ; }
a[2] = function (x) { return x + 2 ; }

In Wirklichkeit wird a folgendermaßen initialisiert, da der letzte Wert von i im Kontext 2 ist:

a[0] = function (x) { return x + 2 ; }
a[1] = function (x) { return x + 2 ; }
a[2] = function (x) { return x + 2 ; }

Die Lösung ist:

var a = new Array();

for (var i=0; i<2; i++) {
    a[i]= function(tmp) {
        return function (x) { return x + tmp ; }
    } (i);
}

Das Argument/variable tmp enthält eine lokale Kopie des sich ändernden Werts von i beim Erstellen von Funktionsinstanzen.

18

Das folgende Beispiel ist eine einfache Darstellung eines JavaScript-Verschlusses. Dies ist die Closure-Funktion, die eine Funktion mit Zugriff auf ihre lokale Variable x zurückgibt.

function outer(x){
     return function inner(y){
         return x+y;
     }
}

Rufen Sie die Funktion folgendermaßen auf:

var add10 = outer(10);
add10(20); // The result will be 30
add10(40); // The result will be 50

var add20 = outer(20);
add20(20); // The result will be 40
add20(40); // The result will be 60
18
Mohammed Safeer

Eine Schließung wird von vielen JavaScript-Entwicklern ständig verwendet, aber wir gehen davon aus, dass dies eine Selbstverständlichkeit ist. Wie es funktioniert ist nicht so kompliziert. Zu verstehen, wie man es gezielt einsetzt , ist komplex.

In seiner einfachsten Definition (wie andere Antworten gezeigt haben) ist ein Abschluss im Grunde eine Funktion, die in einer anderen Funktion definiert ist. Und diese innere Funktion hat Zugriff auf Variablen, die im Bereich der äußeren Funktion definiert sind. Die gängigste Vorgehensweise bei der Verwendung von Closures besteht darin, Variablen und Funktionen im globalen Bereich zu definieren und im Funktionsbereich dieser Funktion auf diese Variablen zuzugreifen.

var x = 1;
function myFN() {
  alert(x); //1, as opposed to undefined.
}
// Or
function a() {
   var x = 1;
   function b() {
       alert(x); //1, as opposed to undefined.
   }
   b();
}

Na und?

Eine Schließung ist für einen JavaScript-Benutzer erst dann etwas Besonderes, wenn Sie darüber nachdenken, wie das Leben ohne sie aussehen würde. In anderen Sprachen werden in einer Funktion verwendete Variablen bereinigt, wenn diese Funktion zurückgegeben wird. Oben wäre x ein "Nullzeiger" gewesen, und Sie müssten einen Getter und Setter einrichten und mit der Übergabe von Referenzen beginnen. Klingt nicht wie JavaScript richtig? Vielen Dank für die mächtige Schließung.

Warum sollte es mich kümmern?

Sie müssen sich der Verschlüsse nicht wirklich bewusst sein, um sie zu verwenden. Aber wie auch andere darauf hingewiesen haben, können sie genutzt werden, um faux private Variablen zu erstellen. Verwenden Sie private Variablen so lange, bis Sie sie benötigen.

17
Harry Robbins

Ich fand sehr klar Kapitel 8, Abschnitt 6, "Abschlüsse" von JavaScript: The Definitive Guide von David Flanagan, 6. Auflage, O'Reilly, 2011. Ich werde versuchen, es zu umschreiben.

  1. Wenn eine Funktion aufgerufen wird, wird ein neues Objekt erstellt, das die lokalen Variablen für diesen Aufruf enthält.

  2. Der Umfang einer Funktion hängt von ihrem Deklarationsort ab, nicht von ihrem Ausführungsort.

Nehmen wir nun eine innere Funktion an, die in einer äußeren Funktion deklariert ist und sich auf Variablen dieser äußeren Funktion bezieht. Angenommen, die äußere Funktion gibt die innere Funktion als Funktion zurück. Jetzt gibt es einen externen Verweis auf alle Werte im Gültigkeitsbereich der inneren Funktion (der nach unseren Annahmen Werte aus der äußeren Funktion enthält).

JavaScript behält diese Werte bei, da sie im Rahmen der aktuellen Ausführung erhalten geblieben sind, da sie aus der abgeschlossenen äußeren Funktion herausgereicht wurden. Alle Funktionen sind Closures, aber die Closures von Interesse sind die inneren Funktionen, die in unserem angenommenen Szenario die äußeren Funktionswerte innerhalb ihrer "Enclosure" (ich hoffe, dass ich hier die Sprache richtig verwende) beibehalten, wenn sie (die inneren Funktionen) zurückgegeben werden von äußeren Funktionen. Ich weiß, dass dies nicht die sechsjährige Anforderung erfüllt, aber es ist hoffentlich immer noch hilfreich.

17
Jim

Eine Funktion wird im Umfang des Objekts/der Funktion ausgeführt, in dem sie definiert ist. Diese Funktion kann auf die Variablen zugreifen, die in dem Objekt/der Funktion definiert sind, in dem/der sie definiert wurden, während sie ausgeführt wird.

Und nehmen Sie es einfach wörtlich ... wie der Code geschrieben steht: P

16
moha297

Wenn Sie es einem sechsjährigen Kind erklären möchten, müssen Sie etwas sehr viel einfacheres und KEINEN Code finden.

Sagen Sie dem Kind einfach, dass es "offen" ist, was besagt, dass es in der Lage ist, mit einigen anderen, seinen Freunden, Beziehungen zu haben. Irgendwann hat er Freunde bestimmt (wir können die Namen seiner Freunde kennen), das ist ein Abschluss. Wenn Sie ein Foto von ihm und seinen Freunden machen, ist er im Verhältnis zu seiner Freundschaftsfähigkeit "geschlossen". Aber im Allgemeinen ist er "offen". Während seines ganzen Lebens wird er viele verschiedene Freundeskreise haben. Eines dieser Sets ist ein Verschluss.

15

Ich bin mir sicher, Einstein hat es nicht mit der direkten Erwartung gesagt, dass wir uns irgendein esoterisches Brainstormer-Ding aussuchen und Sechsjährige mit vergeblichen Versuchen überfahren, diese "Verrückten" zu machen (und was auch immer) ist noch schlimmer für sie - langweilige Dinge für ihren kindlichen Verstand :) Wenn ich sechs Jahre alt wäre, hätte ich keine solchen Eltern oder würde keine Freundschaft mit solchen langweiligen Philanthropen schließen, sorry :)

Wie auch immer, für Babys ist der Verschluss einfach eine Umarmung , denke ich, Was auch immer du versuchst zu erklären :) Und wenn du einen Freund von dir umarmst, dann teilt ihr beide irgendwie alles, was ihr im Moment habt. Es ist ein Übergangsritus, wenn Sie jemanden einmal umarmt haben, zeigen Sie ihr Vertrauen und ihre Bereitschaft, sie eine Menge Dinge mit sich machen zu lassen, die Sie nicht zulassen und vor anderen verstecken würden. Es ist ein Akt der Freundschaft :).

Ich weiß wirklich nicht, wie ich es 5-6-jährigen Babys erklären soll. Ich glaube auch nicht, dass sie JavaScript-Code-Schnipsel wie die folgenden schätzen werden:

function Baby(){
    this.iTrustYou = true;
}

Baby.prototype.hug = function (baby) {
    var smiles = 0;

    if (baby.iTrustYou) {
        return function() {
            smiles++;
            alert(smiles);
        };
    }
};

var
   arman = new Baby("Arman"),
   morgan = new Baby("Morgana");

var hug = arman.hug(morgan);
hug();
hug();

Nur für Kinder:

Closure ist hug

Bug ist fly

KISS is smooch! :)

15

Closures sind ein Mittel, mit dem sich innere Funktionen auf die Variablen beziehen können, die in ihrer äußeren einschließenden Funktion vorhanden sind, nachdem ihre übergeordneten Funktionen bereits beendet wurden.

// A function that generates a new function for adding numbers.
function addGenerator( num ) {
    // Return a simple function for adding two numbers
    // with the first number borrowed from the generator
    return function( toAdd ) {
        return num + toAdd
    };
}

// addFive now contains a function that takes one argument,
// adds five to it, and returns the resulting number.
var addFive = addGenerator( 5 );
// We can see here that the result of the addFive function is 9,
// when passed an argument of 4.
alert( addFive( 4 ) == 9 );
14
ketan

Wenn Sie es gut verstehen, können Sie es einfach erklären. Und der einfachste Weg ist es, es aus dem Kontext zu abstrahieren. Code beiseite, sogar Programmierung beiseite. Ein Metapher-Beispiel wird es besser machen.

Stellen wir uns vor, dass eine Funktion ein Raum ist, dessen Wände aus Glas bestehen, bei dem es sich jedoch um spezielles Glas handelt, wie es in einem Verhörraum der Fall ist. Von außen sind sie undurchsichtig, von innen durchsichtig. Es können Räume in anderen Räumen sein, und der einzige Kontakt ist ein Telefon.

Wenn Sie von außen anrufen, wissen Sie nicht, was sich darin befindet, aber Sie wissen, dass die Personen im Inneren eine Aufgabe erledigen, wenn Sie ihnen bestimmte Informationen geben. Sie können draußen sehen, also können sie Sie nach Dingen fragen, die draußen sind, und Änderungen an diesen Dingen vornehmen, aber Sie können nicht ändern, was innen ist, Sie sehen (wissen) nicht einmal, was innen ist. Die Leute in dem Raum, den Sie anrufen, sehen, was es draußen ist, aber nicht, was es in den Räumen in diesem Raum ist, also interagieren sie mit ihnen so, wie Sie es von außen tun. Die Menschen in den innersten Räumen können viele Dinge sehen, aber die Menschen im äußersten Raum wissen nicht einmal, dass es die innersten Räume gibt.

Bei jedem Anruf in einen Innenraum zeichnen die Personen in diesem Raum die Informationen zu diesem bestimmten Anruf auf, und sie sind so gut darin, dass sie niemals einen Anruf mit einem anderen verwechseln.

Räume sind Funktionen, Sichtbarkeit ist Umfang, Personen, die Aufgaben erledigen, Dinge sind Objekte, Telefonanrufe sind Funktionsaufrufe, Telefonanrufinformationen sind Argumente, Anrufaufzeichnungen sind Bereichsinstanzen, der äußerste Raum ist das globale Objekt.

13
Juan Garcia

Ein Abschluss ist eine Funktion, die Zugriff auf den übergeordneten Bereich hat, auch nachdem die übergeordnete Funktion geschlossen wurde.

var add = (function() {
  var counter = 0;
  return function() {
    return counter += 1;
  }
})();

add();
add();
add();
// The counter is now 3

Beispiel erklärt:

  • Der Variablen add wird der Rückgabewert einer selbstaufrufenden Funktion zugewiesen.
  • Die selbstaufrufende Funktion wird nur einmal ausgeführt. Es setzt den Zähler auf Null (0) und gibt einen Funktionsausdruck zurück.
  • Auf diese Weise wird add zu einer Funktion. Der "wunderbare" Teil ist, dass er auf den Zähler im übergeordneten Bereich zugreifen kann.
  • Dies wird als JavaScript-Schließung bezeichnet. Es ermöglicht einer Funktion, "private" Variablen zu haben.
  • Der Zähler ist durch den Umfang der anonymen Funktion geschützt und kann nur mit der Funktion add geändert werden.

Quelle

13
Premraj

Stellen Sie sich vor, es gibt einen sehr großen Park in Ihrer Stadt, in dem Sie einen Zauberer namens Mr. Coder sehen, der mit seinem Zauberstab, dem JavaScript, in verschiedenen Winkeln des Parks Baseballspiele startet.

Natürlich hat jedes Baseballspiel genau die gleichen Regeln und jedes Spiel hat eine eigene Anzeigetafel.

Natürlich sind die Ergebnisse eines Baseballspiels von den anderen Spielen völlig getrennt.

Ein Abschluss ist die besondere Art und Weise, wie Mr.Coder die Wertung aller seiner magischen Baseballspiele getrennt hält.

13
b_dev

Pinocchio: Schließungen 1883 (über ein Jahrhundert vor JavaScript)

Ich denke, es lässt sich am besten einem 6-Jährigen mit einem schönen Abenteuer erklären ... Der Teil des Abenteuer von Pinocchio , in dem Pinocchio von einem übergroßen Katzenhai verschluckt wird ...

var tellStoryOfPinocchio = function(original) {

  // Prepare for exciting things to happen
  var pinocchioFindsMisterGeppetto;
  var happyEnding;

  // The story starts where Pinocchio searches for his 'father'
  var pinocchio = {
    name: 'Pinocchio',
    location: 'in the sea',
    noseLength: 2
  };

  // Is it a dog... is it a fish...
  // The dogfish appears, however there is no such concept as the belly
  // of the monster, there is just a monster...
  var terribleDogfish = {
    swallowWhole: function(snack) {
      // The swallowing of Pinocchio introduces a new environment (for the
      // things happening inside it)...
      // The BELLY closure... with all of its guts and attributes
      var mysteriousLightLocation = 'at Gepetto\'s ship';

      // Yes: in my version of the story the monsters mouth is directly
      // connected to its belly... This might explain the low ratings
      // I had for biology...
      var mouthLocation = 'in the monsters mouth and then outside';

      var puppet = snack;


      puppet.location = 'inside the belly';
      alert(snack.name + ' is swallowed by the terrible dogfish...');

      // Being inside the belly, Pinocchio can now experience new adventures inside it
      pinocchioFindsMisterGeppetto = function() {
        // The event of Pinocchio finding Mister Geppetto happens inside the
        // belly and so it makes sence that it refers to the things inside
        // the belly (closure) like the mysterious light and of course the
        // hero Pinocchio himself!
        alert(puppet.name + ' sees a mysterious light (also in the belly of the dogfish) in the distance and swims to it to find Mister Geppetto! He survived on ship supplies for two years after being swallowed himself. ');
        puppet.location = mysteriousLightLocation;

        alert(puppet.name + ' tells Mister Geppetto he missed him every single day! ');
        puppet.noseLength++;
      }

      happyEnding = function() {
        // The escape of Pinocchio and Mister Geppetto happens inside the belly:
        // it refers to Pinocchio and the mouth of the beast.
        alert('After finding Mister Gepetto, ' + puppet.name + ' and Mister Gepetto travel to the mouth of the monster.');
        alert('The monster sleeps with its mouth open above the surface of the water. They escape through its mouth. ');
        puppet.location = mouthLocation;
        if (original) {
          alert(puppet.name + ' is eventually hanged for his innumerable faults. ');
        } else {
          alert(puppet.name + ' is eventually turned into a real boy and they all lived happily ever after...');
        }
      }
    }
  }

  alert('Once upon a time...');
  alert('Fast forward to the moment that Pinocchio is searching for his \'father\'...');
  alert('Pinocchio is ' + pinocchio.location + '.');
  terribleDogfish.swallowWhole(pinocchio);
  alert('Pinocchio is ' + pinocchio.location + '.');
  pinocchioFindsMisterGeppetto();
  alert('Pinocchio is ' + pinocchio.location + '.');
  happyEnding();
  alert('Pinocchio is ' + pinocchio.location + '.');

  if (pinocchio.noseLength > 2)
    console.log('Hmmm... apparently a little white lie was told. ');
}

tellStoryOfPinocchio(false);

 
13
Ron Deijkers

Nachdem eine Funktion aufgerufen wurde, verlässt sie den Gültigkeitsbereich. Wenn diese Funktion so etwas wie eine Rückruffunktion enthält, befindet sich diese Rückruffunktion noch im Geltungsbereich. Wenn die Rückruffunktion auf eine lokale Variable in der unmittelbaren Umgebung der übergeordneten Funktion verweist, würden Sie natürlich erwarten, dass diese Variable für die Rückruffunktion nicht zugänglich ist und undefiniert zurückgibt.

Closures stellen sicher, dass alle Eigenschaften, auf die von der Rückruffunktion verwiesen wird, von dieser Funktion verwendet werden können, auch wenn die übergeordnete Funktion möglicherweise nicht mehr gültig ist.

13
goonerify

Vielleicht sollten Sie eine objektorientierte Struktur anstelle innerer Funktionen in Betracht ziehen. Zum Beispiel:

var calculate = {
    number: 0,
    init: function (num) {
        this.number = num;
    },
    add: function (val) {
        this.number += val;
    },
    rem: function (val) {
        this.number -= val;
    }
};

Und lesen Sie das Ergebnis aus der Variable berechne.nummer, die ohnehin "return" benötigt.

12
Psy Chip

Ein Abschluss ist ein Codeblock, der drei Kriterien erfüllt:

  • Es kann als Wert und herumgereicht werden

  • auf Anfrage von jedem ausgeführt, der diesen Wert hat, zu welcher Zeit

  • es kann sich auf Variablen aus dem Kontext beziehen, in dem es erstellt wurde (dh es ist in Bezug auf den Variablenzugriff im mathematischen Sinne des Wortes "geschlossen" geschlossen).

(Das Wort "Abschluß" hat tatsächlich eine ungenaue Bedeutung, und einige Leute glauben nicht, daß Kriterium Nr. 1 Teil der Definition ist. Ich denke, das ist es.)

Verschlüsse sind eine Hauptstütze der funktionalen Sprachen, aber sie sind auch in vielen anderen Sprachen vorhanden (zum Beispiel in Javas anonymen inneren Klassen). Sie können coole Sachen mit ihnen machen: Sie erlauben eine verzögerte Ausführung und einige elegante Tricks des Stils.

Von: Paul Cantrell, @ http://innig.net/software/Ruby/closures-in-Ruby

12
Magne

Verschlüsse sind einfach

Sie sollten einem Sechsjährigen wahrscheinlich nichts über Closures erzählen, aber wenn Sie dies tun, können Sie sagen, dass Closures die Möglichkeit bieten, auf eine Variable zuzugreifen, die in einem anderen Funktionsumfang deklariert wurde.

enter image description here

function getA() {
  var a = [];

  // this action happens later,
  // after the function returned
  // the `a` value
  setTimeout(function() {
    a.splice(0, 0, 1, 2, 3, 4, 5);
  });

  return a;
}

var a = getA();
out('What is `a` length?');
out('`a` length is ' + a.length);

setTimeout(function() {
  out('No wait...');
  out('`a` length is ' + a.length);
  out('OK :|')
});
<pre id="output"></pre>

<script>
  function out(k) {
    document.getElementById('output').innerHTML += '> ' + k + '\n';
  }
</script>
12
Eugene Tiurin

Ein Abschluss wird erstellt, wenn die innere Funktion für einen beliebigen Bereich außerhalb der äußeren Funktion verfügbar gemacht wird.

Beispiel:

var outer = function(params){ //Outer function defines a variable called params
    var inner = function(){ // Inner function has access to the params variable of the outer function
        return params;
    }
    return inner; //Return inner function exposing it to outer scope
},
myFunc = outer("myParams");
myFunc(); //Returns "myParams"
11

Closures sind ein etwas fortgeschrittenes und oft missverstandenes Merkmal der JavaScript-Sprache. Einfach ausgedrückt sind Abschlüsse Objekte, die eine Funktion und einen Verweis auf die Umgebung enthalten, in der die Funktion erstellt wurde. Zum vollständigen Verständnis von Closures müssen jedoch zwei weitere Merkmale der JavaScript-Sprache verstanden werden: erstklassige Funktionen und innere Funktionen.

Erstklassige Funktionen

In Programmiersprachen gelten Funktionen als Bürger erster Klasse, wenn sie wie jeder andere Datentyp manipuliert werden können. Zum Beispiel können erstklassige Funktionen zur Laufzeit erstellt und Variablen zugewiesen werden. Sie können auch an andere Funktionen übergeben und von diesen zurückgegeben werden. JavaScript-Funktionen erfüllen nicht nur die oben genannten Kriterien, sondern verfügen auch über eigene Eigenschaften und Methoden. Das folgende Beispiel zeigt einige der Funktionen erstklassiger Funktionen. Im Beispiel werden zwei Funktionen angelegt und den Variablen „foo“ und „bar“ zugewiesen. Die in "foo" gespeicherte Funktion zeigt ein Dialogfeld an, während "bar" einfach das Argument zurückgibt, das an sie übergeben wird. In der letzten Zeile des Beispiels werden verschiedene Aufgaben ausgeführt. Zunächst wird die in "bar" gespeicherte Funktion mit "foo" als Argument aufgerufen. "Bar" gibt dann die Funktionsreferenz "foo" zurück. Schließlich wird die zurückgegebene "foo" -Referenz aufgerufen, wodurch "Hello World!" Angezeigt wird.

var foo = function() {
  alert("Hello World!");
};

var bar = function(arg) {
  return arg;
};

bar(foo)();

Innere Funktionen

Innere Funktionen, auch als verschachtelte Funktionen bezeichnet, sind Funktionen, die innerhalb einer anderen Funktion definiert sind (als äußere Funktion bezeichnet). Bei jedem Aufruf der äußeren Funktion wird eine Instanz der inneren Funktion erstellt. Das folgende Beispiel zeigt, wie innere Funktionen verwendet werden. In diesem Fall ist add () die äußere Funktion. Innerhalb von add () wird die innere Funktion doAdd () definiert und aufgerufen.

function add(value1, value2) {
  function doAdd(operand1, operand2) {
    return operand1 + operand2;
  }

  return doAdd(value1, value2);
}

var foo = add(1, 2);
// foo equals 3

Ein wichtiges Merkmal innerer Funktionen ist, dass sie implizit Zugriff auf den Gültigkeitsbereich der äußeren Funktion haben. Dies bedeutet, dass die innere Funktion die Variablen, Argumente usw. der äußeren Funktion verwenden kann. Im vorherigen Beispiel wurden die Argumente "Wert1" und "Wert2" von add () an doAdd () als "-" übergeben. operand1 ”und“ operand2 ”Argumente. Dies ist jedoch nicht erforderlich, da doAdd () direkten Zugriff auf "value1" und "value2" hat. Das vorherige Beispiel wurde unten umgeschrieben, um zu zeigen, wie doAdd () "value1" und "value2" verwenden kann.

function add(value1, value2) {
  function doAdd() {
    return value1 + value2;
  }

  return doAdd();
}

var foo = add(1, 2);
// foo equals 3

Erstellen von Abschlüssen

Ein Abschluss wird erstellt, wenn eine innere Funktion von außerhalb der Funktion, die sie erstellt hat, zugänglich gemacht wird. Dies tritt normalerweise auf, wenn eine äußere Funktion eine innere Funktion zurückgibt. In diesem Fall behält die innere Funktion einen Verweis auf die Umgebung bei, in der sie erstellt wurde. Dies bedeutet, dass alle Variablen (und ihre Werte) gespeichert werden, die sich zu diesem Zeitpunkt im Gültigkeitsbereich befanden. Das folgende Beispiel zeigt, wie ein Abschluss erstellt und verwendet wird.

function add(value1) {
  return function doAdd(value2) {
    return value1 + value2;
  };
}

var increment = add(1);
var foo = increment(2);
// foo equals 3

Zu diesem Beispiel gibt es eine Reihe von Punkten zu beachten.

Die Funktion add () gibt ihre innere Funktion doAdd () zurück. Durch die Rückgabe eines Verweises auf eine innere Funktion wird ein Abschluß erzeugt. "Value1" ist eine lokale Variable von add () und eine nicht-lokale Variable von doAdd (). Nicht lokale Variablen beziehen sich auf Variablen, die weder im lokalen noch im globalen Bereich liegen. "Value2" ist eine lokale Variable von doAdd (). Wenn add (1) aufgerufen wird, wird ein Abschluss erstellt und in "Inkrement" gespeichert. In der Referenzierungsumgebung des Abschlusses ist "Wert1" an den Wert Eins gebunden. Variablen, die gebunden sind, werden auch als geschlossen bezeichnet. Hier kommt die Namensschließung her. Beim Aufrufen von Inkrement (2) wird der Abschluss eingegeben. Dies bedeutet, dass doAdd () aufgerufen wird, wobei die Variable „value1“ den Wert eins enthält. Der Verschluss kann im Wesentlichen als Erzeugung der folgenden Funktion angesehen werden.

function increment(value2) {
  return 1 + value2;
}

Wann werden Verschlüsse verwendet?

Verschlüsse können verwendet werden, um viele Dinge zu erreichen. Sie sind sehr nützlich, um beispielsweise Rückruffunktionen mit Parametern zu konfigurieren. In diesem Abschnitt werden zwei Szenarien behandelt, in denen Abschlüsse Ihr Leben als Entwickler erheblich vereinfachen können.

Mit Timern arbeiten

Closures sind nützlich, wenn sie in Verbindung mit den Funktionen setTimeout () und setInterval () verwendet werden. Genauer gesagt können Sie mit Closures Argumente an die Rückruffunktionen von setTimeout () und setInterval () übergeben. Mit dem folgenden Code wird beispielsweise die Zeichenfolge "some message" einmal pro Sekunde gedruckt, indem showMessage () aufgerufen wird.

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Closures</title>
  <meta charset="UTF-8" />
  <script>
    window.addEventListener("load", function() {
      window.setInterval(showMessage, 1000, "some message<br />");
    });

    function showMessage(message) {
      document.getElementById("message").innerHTML += message;
    }
  </script>
</head>
<body>
  <span id="message"></span>
</body>
</html>

Leider unterstützt Internet Explorer die Übergabe von Rückrufargumenten über setInterval () nicht. Internet Explorer zeigt nicht "irgendeine Nachricht" an, sondern "undefiniert" (da tatsächlich kein Wert an showMessage () übergeben wird). Um dieses Problem zu umgehen, kann ein Abschluss erstellt werden, der das Argument "message" an den gewünschten Wert bindet. Der Abschluß kann dann als Rückruffunktion für setInterval () verwendet werden. Um dieses Konzept zu veranschaulichen, wurde der JavaScript-Code aus dem vorherigen Beispiel unten umgeschrieben, um einen Abschluss zu verwenden.

window.addEventListener("load", function() {
  var showMessage = getClosure("some message<br />");

  window.setInterval(showMessage, 1000);
});

function getClosure(message) {
  function showMessage() {
    document.getElementById("message").innerHTML += message;
  }

  return showMessage;
}

Private Daten emulieren

Viele objektorientierte Sprachen unterstützen das Konzept der privaten Mitgliedsdaten. JavaScript ist jedoch keine reine objektorientierte Sprache und unterstützt keine privaten Daten. Es ist jedoch möglich, private Daten mithilfe von Closures zu emulieren. Erinnern Sie sich daran, dass ein Abschluss einen Verweis auf die Umgebung enthält, in der er ursprünglich erstellt wurde - was jetzt außerhalb des Geltungsbereichs liegt. Da die Variablen in der Referenzierungsumgebung nur über die Schließfunktion zugänglich sind, handelt es sich im Wesentlichen um private Daten.

Das folgende Beispiel zeigt einen Konstruktor für eine einfache Person-Klasse. Wenn jede Person erstellt wird, erhält sie über das Argument "Name" einen Namen. Intern speichert die Person ihren Namen in der Variablen "_ name". Gemäß guter objektorientierter Programmierpraktiken wird auch die Methode getName () zum Abrufen des Namens bereitgestellt.

function Person(name) {
  this._name = name;

  this.getName = function() {
    return this._name;
  };
}

Es gibt immer noch ein großes Problem mit der Personenklasse. Da JavaScript keine privaten Daten unterstützt, hindert nichts andere daran, mitzukommen und den Namen zu ändern. Mit dem folgenden Code wird beispielsweise eine Person mit dem Namen Colin erstellt und anschließend in Tom umbenannt.

var person = new Person("Colin");

person._name = "Tom";
// person.getName() now returns "Tom"

Persönlich würde es mir nicht gefallen, wenn irgendjemand mitkommen und meinen Namen legal ändern könnte. Um dies zu verhindern, kann die Variable "_name" durch einen Abschluss als privat deklariert werden. Der Person-Konstruktor wurde unten mit einem Closure umgeschrieben. Beachten Sie, dass "_name" jetzt eine lokale Variable des Person-Konstruktors anstelle einer Objekteigenschaft ist. Ein Abschluss wird gebildet, weil die äußere Funktion Person () eine innere Funktion durch Erstellen der öffentlichen getName () -Methode verfügbar macht.

function Person(name) {
  var _name = name;

  this.getName = function() {
    return _name;
  };
}

Wenn getName () aufgerufen wird, wird garantiert der Wert zurückgegeben, der ursprünglich an den Konstruktor übergeben wurde. Es ist weiterhin möglich, dem Objekt eine neue Eigenschaft "_name" hinzuzufügen, aber die interne Funktionsweise des Objekts wird nicht beeinflusst, solange sie sich auf die durch den Abschluss gebundene Variable bezieht. Der folgende Code zeigt, dass die Variable "_name" tatsächlich privat ist.

var person = new Person("Colin");

person._name = "Tom";
// person._name is "Tom" but person.getName() returns "Colin"

Wann sollten Verschlüsse nicht verwendet werden?

Es ist wichtig zu verstehen, wie Verschlüsse funktionieren und wann sie verwendet werden. Genauso wichtig ist es zu verstehen, wenn sie nicht das richtige Werkzeug für den jeweiligen Job sind. Übermäßige Verwendung von Schließungen kann dazu führen, dass Skripts langsam ausgeführt werden und unnötigen Speicher belegen. Und weil Verschlüsse so einfach zu erstellen sind, ist es möglich, sie zu missbrauchen, ohne es zu wissen. In diesem Abschnitt werden verschiedene Szenarien behandelt, in denen Verschlüsse mit Vorsicht verwendet werden sollten.

In Loops

Das Erstellen von Abschlüssen in Schleifen kann zu irreführenden Ergebnissen führen. Ein Beispiel dafür ist unten gezeigt. In diesem Beispiel werden drei Schaltflächen erstellt. Wenn auf "button1" geklickt wird, sollte eine Warnung mit der Aufschrift "Clicked button 1" angezeigt werden. Ähnliche Meldungen sollten für "button2" und "button3" angezeigt werden. Wenn dieser Code ausgeführt wird, wird auf allen Schaltflächen jedoch "Angeklickte Schaltfläche 4" angezeigt. Dies liegt daran, dass die Schleife bis zum Klicken auf eine der Schaltflächen vollständig ausgeführt wurde und die Schleifenvariable ihren Endwert von vier erreicht hat.

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Closures</title>
  <meta charset="UTF-8" />
  <script>
    window.addEventListener("load", function() {
      for (var i = 1; i < 4; i++) {
        var button = document.getElementById("button" + i);

        button.addEventListener("click", function() {
          alert("Clicked button " + i);
        });
      }
    });
  </script>
</head>
<body>
  <input type="button" id="button1" value="One" />
  <input type="button" id="button2" value="Two" />
  <input type="button" id="button3" value="Three" />
</body>
</html>

Um dieses Problem zu lösen, muss der Verschluss von der eigentlichen Schleifenvariablen abgekoppelt werden. Dies kann durch Aufrufen einer neuen Funktion erfolgen, wodurch wiederum eine neue Referenzierungsumgebung erstellt wird. Das folgende Beispiel zeigt, wie das gemacht wird. Die Schleifenvariable wird an die Funktion getHandler () übergeben. getHandler () gibt dann einen Abschluss zurück, der von der ursprünglichen for-Schleife unabhängig ist.

function getHandler(i) {
  return function handler() {
    alert("Clicked button " + i);
  };
}
window.addEventListener("load", function() {
  for (var i = 1; i < 4; i++) {
    var button = document.getElementById("button" + i);
    button.addEventListener("click", getHandler(i));
  }
});

Unnötige Verwendung in Konstruktoren

Konstruktorfunktionen sind eine weitere häufige Ursache für den Missbrauch von Schließungen. Wir haben gesehen, wie Schließungen zum Emulieren privater Daten verwendet werden können. Es ist jedoch zu viel des Guten, Methoden als Closures zu implementieren, wenn sie nicht auf die privaten Daten zugreifen. Im folgenden Beispiel wird die Person-Klasse erneut aufgerufen. Diesmal wird jedoch eine sayHello () -Methode hinzugefügt, die die privaten Daten nicht verwendet.

function Person(name) {
  var _name = name;

  this.getName = function() {
    return _name;
  };

  this.sayHello = function() {
    alert("Hello!");
  };
}

Jedes Mal, wenn eine Person instanziiert wird, wird Zeit für die Erstellung der sayHello () -Methode aufgewendet. Wenn viele Personenobjekte erstellt werden, wird dies zu einer Zeitverschwendung. Ein besserer Ansatz wäre das Hinzufügen von sayHello () zum Person-Prototyp. Durch Hinzufügen zum Prototyp können alle Personenobjekte dieselbe Methode verwenden. Dies spart Zeit im Konstruktor, da nicht für jede Instanz ein Abschluss erstellt werden muss. Das vorherige Beispiel wird im Folgenden umgeschrieben, wobei der überflüssige Verschluss in den Prototyp verschoben wird.

function Person(name) {
  var _name = name;

  this.getName = function() {
    return _name;
  };
}

Person.prototype.sayHello = function() {
  alert("Hello!");
};

Dinge, an die Sie sich erinnern sollten

  • Abschlüsse enthalten eine Funktion und einen Verweis auf die Umgebung, in der die Funktion erstellt wurde.
  • Ein Verschluss entsteht, wenn eine äußere Funktion eine innere Funktion freigibt. Mit Closures können Parameter einfach an Callback-Funktionen übergeben werden.
  • Private Daten können mithilfe von Closures emuliert werden. Dies ist in der objektorientierten Programmierung und im Namespace-Design üblich.
  • Verschlüsse sollten in Konstrukteuren nicht überbeansprucht werden. Das Hinzufügen zum Prototyp ist eine bessere Idee.

Link

11
Durgesh Pandey

Wenn man bedenkt, dass es bei der Frage darum geht, sie einfach so zu erklären, als ob sie einem 6-Jährigen ginge, würde meine Antwort lauten:

"Wenn Sie eine Funktion in JavaScript deklarieren, hat sie für immer Zugriff auf alle Variablen und Funktionen, die in der Zeile vor dieser Funktionsdeklaration verfügbar waren. Die Funktion und alle äußeren Variablen und Funktionen, auf die sie Zugriff hat, sind das, worauf wir zugreifen Schließung nennen. "

10
Raul Martins

Funktionen, die keine freien Variablen enthalten, heißen reine Funktionen.

Funktionen, die eine oder mehrere freie Variablen enthalten, werden als Abschlüsse bezeichnet.

var pure = function pure(x){
  return x 
  // only own environment is used
}

var foo = "bar"

var closure = function closure(){
  return foo
  // foo is free variable from the outer environment
}

src: https://leanpub.com/javascriptallongesix/read#leanpub-auto-if-functions-without-free-variables-are-pure-are-closures-impure

10
soundyogi

MDN erklärt es am besten, denke ich:

Closures sind Funktionen, die sich auf unabhängige (freie) Variablen beziehen. Mit anderen Worten, die im Abschluss definierte Funktion „merkt“ sich die Umgebung, in der sie erstellt wurde.

Ein Verschluss hat immer eine äußere und eine innere Funktion. In der inneren Funktion findet die gesamte Arbeit statt, und in der äußeren Funktion bleibt nur die Umgebung erhalten, in der die innere Funktion erstellt wurde. Auf diese Weise „erinnert“ sich die innere Funktion eines Verschlusses an die Umgebung/den Umfang, in dem er erstellt wurde. Das klassischste Beispiel ist eine Zählerfunktion:

var closure = function() {
  var count = 0;
  return function() {
    count++;
    console.log(count);
  };
};

var counter = closure();

counter() // returns 1
counter() // returns 2
counter() // returns 3

Im obigen Code wird count von der äußeren Funktion (Umgebungsfunktion) beibehalten, so dass die innere Funktion (Arbeitsfunktion) sie bei jedem Aufruf von counter() inkrementieren kann.

9
Brandon Kent

Ich mag Kyle Simpsons Definition eines Abschlusses:

Abschluss ist, wenn eine Funktion in der Lage ist, sich an ihren lexikalischen Gültigkeitsbereich zu erinnern und auf diesen zuzugreifen, selbst wenn diese Funktion außerhalb ihres lexikalischen Gültigkeitsbereichs ausgeführt wird.

Lexikalischer Gültigkeitsbereich ist, wenn ein innerer Gültigkeitsbereich auf seinen äußeren Gültigkeitsbereich zugreifen kann.

Hier ist ein modifiziertes Beispiel, das er in seiner Buchreihe "You Don't Know JS: Scopes & Closures" zur Verfügung stellt.

function foo() {
  var a = 2;

  function bar() {
    console.log( a );
  }
  return bar;
}

function test() {
  var bz = foo();
  bz();
}

// prints 2. Here function bar referred by var bz is outside 
// its lexical scope but it can still access it
test(); 
9
TastyCode

Auf diese Weise wickelt ein Anfänger den Kopf um Closures, wie eine Funktion, in einem Funktionskörper, der auch als Closures bezeichnet wird.

Definition aus dem Buch Speaking JavaScript "Ein Abschluss ist eine Funktion plus der Zusammenhang mit dem Umfang, in dem die Funktion erstellt wurde" - Dr.Axel Rauschmayer

Wie könnte das also aussehen? Hier ist ein Beispiel

function newCounter() {
  var counter = 0;
   return function increment() {
    counter += 1;
   }
}

var counter1 = newCounter();
var counter2 = newCounter();

counter1(); // Number of events: 1
counter1(); // Number of events: 2
counter2(); // Number of events: 1
counter1(); // Number of events: 3

newCounter schließt über inkrementieren , Zähler kann referenziert und durch Inkrementieren aufgerufen werden.

counter1 und counter2 verfolgen ihren eigenen Wert.

Einfach, aber hoffentlich eine klare Perspektive, was für ein Abschluss all dieser großartigen und fortgeschrittenen Antworten ist.

8
devlighted

Closure ist, wenn eine Funktion closed ist, so dass sie in einem Namespace definiert wurde, der zum Zeitpunkt des Funktionsaufrufs unveränderlich ist.

In JavaScript passiert es, wenn Sie:

  • Definieren Sie eine Funktion innerhalb der anderen Funktion
  • Die innere Funktion wird aufgerufen, nachdem die äußere Funktion zurückgegeben wurde
// 'name' is resolved in the namespace created for one invocation of bindMessage
// the processor cannot enter this namespace by the time displayMessage is called
function bindMessage(name, div) {

    function displayMessage() {
        alert('This is ' + name);
    }

    $(div).click(displayMessage);
}
8
Pawel Furmaniak

Für einen Sechsjährigen ...

Wissen Sie, was Objekte sind?

Objekte sind Dinge, die Eigenschaften haben und Dinge tun.

Eines der wichtigsten Dinge bei Verschlüssen ist, dass Sie damit Objekte in JavaScript erstellen können. Objekte in JavaScript sind nur Funktionen und Abschlüsse, mit denen JavaScript den Wert der Eigenschaft für das Objekt speichert, sobald es erstellt wurde.

Objekte sind sehr nützlich und halten alles schön und organisiert. Unterschiedliche Objekte können unterschiedliche Aufgaben ausführen, und das Zusammenarbeiten von Objekten kann komplizierte Aufgaben ausführen.

Es ist ein Glück, dass JavaScript Verschlüsse zum Erstellen von Objekten hat, sonst würde alles zu einem Albtraum werden.

8
ejectamenta

Der beste Weg ist, diese Konzepte inkrementell zu erklären:

Variablen

console.log(x);
// undefined

Hier ist undefined die Art und Weise, wie JavaScript sagt: "Ich habe keine Ahnung, was x bedeutet."

Variablen sind wie Tags.

Sie können sagen, Tag x zeigt auf Wert 42:

var x = 42;
console.log(x);
// 42

Jetzt weiß JavaScript, was x bedeutet.

Sie können auch eine Variable neu zuweisen.

Lassen Sie tag x auf einen anderen Wert zeigen:

x = 43;
console.log(x);
// 43

Jetzt bedeutet x etwas anderes.

Geltungsbereich

Wenn Sie eine Funktion erstellen, verfügt die Funktion über eine eigene "Box" für Variablen.

function A() {
  var x = 42;
}

console.log(x);

// undefined

Von außerhalb der Box können Sie nicht sehen, was sich in der Box befindet.

Im Inneren der Box können Sie jedoch sehen, was sich außerhalb der Box befindet:

var x = 42;

function A() {
  console.log(x);
}

// 42

Innerhalb der Funktion A haben Sie "Bereichszugriff" auf x.

Nun, wenn Sie zwei Boxen nebeneinander haben:

function A() {
  var x = 42;
}

function B() {
  console.log(x);
}

// undefined

Innerhalb der Funktion B haben Sie keinen Zugriff auf Variablen innerhalb der Funktion A.

Aber wenn Sie die Funktion B innerhalb der Funktion A definieren:

function A() {

  var x = 42;

  function B() {
    console.log(x);
  }

}

// 42

Sie haben jetzt "Scope Access".

Funktionen

In JavaScript führen Sie eine Funktion aus, indem Sie sie aufrufen:

function A() {
  console.log(42);
}

So was:

A();

// 42

Funktioniert als Werte

In JavaScript können Sie ein Tag auf eine Funktion verweisen, genauso wie auf eine Zahl:

var a = function() {
  console.log(42);
};

Die Variable a bedeutet jetzt eine Funktion, die Sie ausführen können.

a();
// 42

Sie können diese Variable auch weitergeben:

setTimeout(a, 1000);

In einer Sekunde (1000 Millisekunden) wird die Funktion a aufgerufen:

// 42

Closure Scope

Wenn Sie nun Funktionen definieren, haben diese Funktionen Zugriff auf ihre äußeren Bereiche.

Wenn Sie Funktionen als Werte übergeben, ist es problematisch, wenn dieser Zugriff verloren geht.

In JavaScript behalten Funktionen ihren Zugriff auf äußere Bereichsvariablen. Sogar wenn sie herumgereicht werden, um woanders ausgeführt zu werden.

var a = function() {

  var text = 'Hello!'

  var b = function() {
    console.log(text);
    // inside function `b`, you have access to `text`
  };

  // but you want to run `b` later, rather than right away
  setTimeout(b, 1000);

}

Was passiert jetzt?

// 'Hello!'

Oder überlegen Sie:

var c;

var a = function() {

  var text = 'Hello!'

  var b = function() {
    console.log(text);
    // inside function `b`, you have access to `text`
  };

  c = b;

}

// now we are out side of function `a`
// call `a` so the code inside `a` runs
a(); 

// now `c` has a value that is a function
// because what happened when `a` ran

// when you run `c`
c();

// 'Hello!'

Sie können weiterhin auf Variablen im Schließungsbereich zugreifen.

Obwohl a beendet wurde und Sie jetzt c außerhalb von a ausführen.

Was hier gerade passiert ist, heißt in JavaScript ' closure '.

7
David Rosson

Abschluss sind nicht schwer zu verstehen. Das hängt nur vom Standpunkt ab.

Ich persönlich benutze sie gerne im täglichen Leben.

function createCar()
{
    var rawMaterial = [/* lots of object */];
    function transformation(rawMaterials)
    {
       /* lots of changement here */
       return transformedMaterial;
    }
    var transformedMaterial = transformation(rawMaterial);
    function assemblage(transformedMaterial)
    {
        /*Assemblage of parts*/
        return car;
    }
    return assemblage(transformedMaterial);
}

Wir müssen nur in bestimmten Fällen bestimmte Schritte ausführen. Wie für die Umwandlung von Materialien ist nur sinnvoll, wenn Sie die Teile haben.

7
Alexis

Es war einmal ein Höhlenmensch

function caveman {

die einen ganz besonderen Stein hatten,

var rock = "diamond";

Sie konnten den Felsen nicht selbst bekommen, weil er sich in der privaten Höhle des Höhlenmeisters befand. Nur der Höhlenmensch wusste, wie man den Stein findet und holt.

return {
    getRock: function() {
        return rock;
    }
};
}

Glücklicherweise war er ein freundlicher Höhlenmensch, und wenn Sie bereit wären, auf seine Rückkehr zu warten, würde er es gerne für Sie besorgen.

var friend = caveman();
var rock = friend.getRock();

Ziemlich kluger Höhlenmensch.

7
NinjaBeetle

Beginnen wir hier, wie in MDN definiert: Closures sind Funktionen, die sich auf unabhängige (freie) Variablen beziehen (Variablen, die sind) lokal verwendet, aber in einem umschließenden Geltungsbereich definiert). Mit anderen Worten, diese Funktionen 'erinnern' sich an die Umgebung, in der sie erstellt wurden.

Lexikalisches Scoping
Folgendes berücksichtigen:

function init() {
  var name = 'Mozilla'; // name is a local variable created by init
  function displayName() { // displayName() is the inner function, a closure
    alert(name); // use variable declared in the parent function    
  }
  displayName();    
}
init();

init () erstellt eine lokale Variable namens name und eine Funktion namens displayName (). Die displayName () - Funktion ist eine innere Funktion, die in init () definiert ist und nur im Hauptteil der init () - Funktion verfügbar ist. Die displayName () - Funktion hat keine eigenen lokalen Variablen. Da innere Funktionen jedoch Zugriff auf die Variablen der äußeren Funktionen haben, kann displayName () auf den in der übergeordneten Funktion init () deklarierten Variablennamen zugreifen.

function init() {
    var name = "Mozilla"; // name is a local variable created by init
    function displayName() { // displayName() is the inner function, a closure
        alert (name); // displayName() uses variable declared in the parent function    
    }
    displayName();    
}
init();

Führen Sie den Code aus, und beachten Sie, dass die alert () - Anweisung in der displayName () - Funktion den Wert der Namensvariablen erfolgreich anzeigt, die in der übergeordneten Funktion deklariert ist. Dies ist ein Beispiel für lexikalisches Scoping, das beschreibt, wie ein Parser Variablennamen auflöst, wenn Funktionen verschachtelt sind. Das Wort "lexikalisch" bezieht sich auf die Tatsache, dass das lexikalische Scoping den Ort verwendet, an dem eine Variable im Quellcode deklariert wird, um zu bestimmen, wo diese Variable verfügbar ist. Verschachtelte Funktionen haben Zugriff auf Variablen, die in ihrem äußeren Gültigkeitsbereich deklariert sind.

Abschluss
Betrachten Sie nun das folgende Beispiel:

function makeFunc() {
  var name = 'Mozilla';
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

Das Ausführen dieses Codes hat genau den gleichen Effekt wie das obige Beispiel der init () - Funktion: Dieses Mal wird die Zeichenfolge "Mozilla" in einem JavaScript-Warnfeld angezeigt. Das Besondere - und Interessante - ist, dass die innere displayName () - Funktion vor der Ausführung von der äußeren zurückgegeben wird.

Auf den ersten Blick mag es uninteressant erscheinen, dass dieser Code immer noch funktioniert. In einigen Programmiersprachen existieren die lokalen Variablen innerhalb einer Funktion nur für die Dauer der Ausführung dieser Funktion. Sobald makeFunc () ausgeführt wurde, können Sie davon ausgehen, dass auf die Namensvariable nicht mehr zugegriffen werden kann. Da der Code jedoch weiterhin wie erwartet funktioniert, ist dies in JavaScript offensichtlich nicht der Fall.

Der Grund dafür ist, dass in JavaScript Formularverschlüsse funktionieren. Ein Closure ist die Kombination einer Funktion und der lexikalischen Umgebung, in der diese Funktion deklariert wurde. Diese Umgebung besteht aus allen lokalen Variablen, die zum Zeitpunkt der Erstellung des Abschlusses im Gültigkeitsbereich waren. In diesem Fall ist myFunc ein Verweis auf die Instanz der Funktion displayName, die beim Ausführen von makeFunc erstellt wird. Die Instanz von displayName enthält einen Verweis auf ihre lexikalische Umgebung, in der sich der Variablenname befindet. Aus diesem Grund bleibt beim Aufrufen von myFunc der Variablenname zur Verwendung verfügbar, und "Mozilla" wird an alert übergeben.

Hier ist ein etwas interessanteres Beispiel - eine makeAdder-Funktion:

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

In diesem Beispiel haben wir eine Funktion makeAdder (x) definiert, die ein einzelnes Argument, x, akzeptiert und eine neue Funktion zurückgibt. Die zurückgegebene Funktion verwendet ein einzelnes Argument, y, und gibt die Summe von x und y zurück.

Im Wesentlichen ist makeAdder eine Funktionsfactory - es erstellt Funktionen, die ihrem Argument einen bestimmten Wert hinzufügen können. Im obigen Beispiel verwenden wir unsere Function Factory, um zwei neue Funktionen zu erstellen - eine, die dem Argument 5 hinzufügt, und eine, die 10 hinzufügt.

add5 und Add10 sind beide Verschlüsse. Sie haben dieselbe Funktionskörperdefinition, speichern jedoch unterschiedliche lexikalische Umgebungen. In der lexikalischen Umgebung von add5 ist x 5, während in der lexikalischen Umgebung von add10 x 10 ist.

Praktische Verschlüsse

Closures sind nützlich, weil Sie damit einige Daten (die lexikalische Umgebung) mit einer Funktion verknüpfen können, die diese Daten verarbeitet. Dies hat offensichtliche Parallelen zur objektorientierten Programmierung, bei der Objekte es uns ermöglichen, einige Daten (die Objekteigenschaften) einer oder mehreren Methoden zuzuordnen.

Folglich können Sie einen Abschluss überall dort verwenden, wo Sie normalerweise ein Objekt mit nur einer Methode verwenden.

Situationen, in denen Sie dies tun möchten, sind im Web besonders häufig. Ein Großteil des Codes, den wir in Front-End-JavaScript schreiben, ist ereignisbasiert. Wir definieren ein bestimmtes Verhalten und hängen es dann an ein vom Benutzer ausgelöstes Ereignis an (z. B. einen Klick oder einen Tastendruck). Unser Code wird im Allgemeinen als Rückruf angehängt: eine einzelne Funktion, die als Reaktion auf das Ereignis ausgeführt wird.

Angenommen, wir möchten einer Seite einige Schaltflächen hinzufügen, mit denen die Textgröße angepasst werden kann. Eine Möglichkeit, dies zu tun, besteht darin, die Schriftgröße des body-Elements in Pixel anzugeben und dann die Größe der anderen Elemente auf der Seite (z. B. Überschriften) mithilfe der relativen em-Einheit festzulegen:

body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}

Mit unseren interaktiven Textgrößenschaltflächen können Sie die Schriftgrößeneigenschaft des Hauptteilelements ändern. Die Anpassungen werden dank der relativen Einheiten von anderen Elementen auf der Seite übernommen. Hier ist das JavaScript:

function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

size12, size14 und size16 sind jetzt Funktionen, mit denen die Größe des Haupttextes auf 12, 14 bzw. 16 Pixel geändert wird. Wir können sie wie folgt an Schaltflächen (in diesem Fall Links) anhängen:

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>


function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

weitere Informationen zu Schließungen finden Sie unter Link auf MDN

6
Alireza

Ich habe all dies in der Vergangenheit gelesen und sie sind alle sehr informativ. Einige kommen der einfachen Erklärung sehr nahe und werden dann komplex oder bleiben abstrakt, machen den Zweck zunichte und zeigen keine sehr einfache Anwendung in der realen Welt.

Wenn Sie alle Beispiele und Erklärungen durchgehen, erhalten Sie eine gute Vorstellung davon, was Abschlüsse sind und was nicht, und zwar über Kommentare und Code. Trotzdem war ich mit einer sehr einfachen Illustration unzufrieden, die mir half, Abschlüsse brauchbar zu machen, ohne so komplex zu werden. Meine Frau möchte das Programmieren lernen und ich dachte, ich muss hier nicht nur zeigen, was, sondern warum und wie.

Ich bin nicht sicher, ob ein Sechsjähriger das bekommt, aber ich denke, es könnte ein bisschen näher sein, einen einfachen Fall in einer realen Welt zu demonstrieren, der tatsächlich nützlich und leicht verständlich sein könnte.

Eines der besten (oder einfachsten) Beispiele ist das Nacherzählen von Morris 'Closures for Dummies.

Wenn Sie das "SayHi2Bob" -Konzept nur einen Schritt weiter verfolgen, werden die beiden grundlegenden Dinge demonstriert, die Sie aus dem Lesen aller Antworten ersehen können:

  1. Closures haben Zugriff auf die Variablen der enthaltenden Funktion.
  2. Schließungen verbleiben in ihrem eigenen Speicherbereich (und sind daher für alle Arten von OOP-Y-Instantiierungs-Dingen nützlich).

Das zu beweisen und mir selbst zu demonstrieren, machte ich ein wenig Geige:

http://jsfiddle.net/9ZMyr/2/

function sayHello(name) {
  var text = 'Hello ' + name; // Local variable
  console.log(text);
  var sayAlert = function () {
      alert(text);
  }
  return sayAlert;
}

sayHello(); 
/* This will write 'Hello undefined' to the console (in Chrome anyway), 
but will not alert though since it returns a function handle to nothing). 
Since no handle or reference is created, I imagine a good js engine would 
destroy/dispose of the internal sayAlert function once it completes. */

// Create a handle/reference/instance of sayHello() using the name 'Bob'
sayHelloBob = sayHello('Bob');
sayHelloBob();

// Create another handle or reference to sayHello with a different name
sayHelloGerry = sayHello('Gerry');
sayHelloGerry();

/* Now calling them again demonstrates that each handle or reference contains its own 
unique local variable memory space. They remain in memory 'forever' 
(or until your computer/browser explode) */
sayHelloBob();
sayHelloGerry();

Dies demonstriert die beiden grundlegenden Konzepte, die Sie für Verschlüsse benötigen.

Um zu erklären, warum dies nützlich ist, habe ich eine Basisfunktion, auf die ich Verweise oder Handles erstellen kann, die eindeutige Daten enthalten, die in dieser Speicherreferenz bestehen bleiben. Ich muss die Funktion nicht jedes Mal neu schreiben, wenn ich den Namen einer Person aussprechen möchte. Ich habe diese Routine zusammengefasst und wiederverwendbar gemacht.

Für mich führt dies zumindest zu den grundlegenden Konzepten von Konstruktoren, OOP-Praktiken, Singletons gegenüber instanziierten Instanzen mit eigenen Daten usw. usw.

Wenn Sie damit einen Neuling anfangen, können Sie zu komplexeren objekteigenschafts-/mitgliederbasierten Aufrufen übergehen, und hoffentlich tragen die Konzepte.

6
williambq

Ich halte es für sinnvoll, einen Schritt zurückzutreten und einen allgemeineren Begriff eines "Abschlusses" zu untersuchen - den sogenannten "Join-Operator".

In der Mathematik ist ein "Verknüpfungs" -Operator eine Funktion für eine teilweise geordnete Menge, die das kleinste Objekt zurückgibt, das größer oder gleich seinen Argumenten ist. In Symbolen verbinde [a, b] = d, so dass d> = a und d> = b, aber es gibt kein e, so dass d> e> = a oder d> e> = b.

Die Verbindung gibt Ihnen also das Kleinste, das "größer" ist als die Teile.

Beachten Sie nun, dass JavaScript-Bereiche eine teilweise geordnete Struktur sind. Damit gibt es eine vernünftige Vorstellung von einem Join. Insbesondere ist eine Verknüpfung von Bereichen der kleinste Bereich, der größer ist als der ursprüngliche Bereich. Dieser Bereich wird als Abschluß bezeichnet.

Ein Abschluss für die Variablen a, b, c ist also der kleinste Bereich (im Bereich Ihres Programms!), Der a, b und c in den Bereich bringt.

6
nomen

Der einfachste Anwendungsfall, den ich mir vorstellen kann, um zu erklären, JavaScript-Schließungen ist das Modulmuster. Im Modulmuster definieren Sie eine Funktion und rufen sie unmittelbar danach in einem sog. Sofort aufgerufenen Funktionsausdruck (IIFE) auf. Alles, was Sie in diese Funktion schreiben, hat einen privaten Gültigkeitsbereich, da es innerhalb des Verschlusses definiert ist, wodurch Sie den Datenschutz in JavaScript "simulieren" können. Wie so:

 var Closure = (function () {
    // This is a closure
    // Any methods, variables and properties you define here are "private"
    // and can't be accessed from outside the function.

    //This is a private variable
    var foo = "";

    //This is a private method
    var method = function(){

    }
})();

Wenn Sie andererseits eine oder mehrere Variablen oder Methoden außerhalb des Abschlusses sichtbar machen möchten, können Sie sie in einem Objektliteral zurückgeben. Wie so:

var Closure = (function () {
  // This is a closure
  // Any methods, variables and properties you define here are "private"
  // and can't be accessed from outside the function.

  //This is a private variable
  var foo = "";

  //This is a private method
  var method = function(){

  }

  //The method will be accessible from outside the closure
  return {
    method: method
  }

})();

Closure.method();

Ich hoffe es hilft. Grüße,

6
Javier La Banca

Das Wort Abschluß bezieht sich einfach darauf, auf Objekte (Sechsjähriger: Dinge) zugreifen zu können, die Abschluß (Sechsjähriger) sind : private) innerhalb eines function (6 Jahre alt: box). Auch wenn Funktion (Sechsjähriger: Kästchen) nicht Gültigkeitsbereich (Sechsjähriger: Weit entfernt gesendet) ist.

6
cube

Ein Abschluss erzeugt im Grunde genommen zwei Dinge: - eine Funktion - einen privaten Bereich, auf den nur diese Funktion zugreifen kann

Es ist, als würde man eine Funktion beschichten.

Für einen Sechsjährigen könnte dies also durch eine Analogie erklärt werden. Nehmen wir an, ich baue einen Roboter. Dieser Roboter kann viele Dinge tun. Unter anderem programmierte ich es, um die Anzahl der Vögel zu zählen, die er am Himmel sieht. Jedes Mal, wenn er 25 Vögel gesehen hat, sollte er mir sagen, wie viele Vögel er von Anfang an gesehen hat.

Ich weiß nicht, wie viele Vögel er gesehen hat, es sei denn, er hat es mir gesagt. Nur er weiß es. Das ist der private Bereich. Das ist im Grunde das Gedächtnis des Roboters. Nehmen wir an, ich habe ihm 4 GB gegeben.

Mir zu sagen, wie viele Vögel er gesehen hat, ist die zurückgegebene Funktion. Das habe ich auch gemacht.

Diese Analogie ist ein bisschen blöd, aber jemand könnte sie verbessern, denke ich.

6
Taye

Meine Sicht auf Closures:

Verschlüsse können mit einem Buch, mit einem Lesezeichen, in einem Bücherregal verglichen werden.

Angenommen, Sie haben ein Buch gelesen und Ihnen gefällt eine Seite im Buch. Sie haben auf dieser Seite ein Lesezeichen eingefügt, um es zu verfolgen.

Sobald Sie das Buch gelesen haben, brauchen Sie es nicht mehr, außer Sie möchten Zugriff auf diese Seite haben. Du hättest die Seite einfach ausschneiden können, aber dann würdest du den Kontext der Geschichte verlieren. So legen Sie das Buch mit dem Lesezeichen zurück in Ihr Bücherregal.

Dies ähnelt einer Schließung. Das Buch ist die äußere Funktion, und die Seite ist Ihre innere Funktion, die von der äußeren Funktion zurückgegeben wird. Das Lesezeichen ist der Verweis auf Ihre Seite, und der Kontext der Geschichte ist der lexikalische Bereich, den Sie beibehalten müssen. Das Bücherregal ist der Funktionsstapel, der nicht von den alten Büchern aufgeräumt werden kann, bis Sie die Seite festhalten.

Codebeispiel:

function book() {
   var pages = [....]; //array of pages in your book
   var bookMarkedPage = 20; //bookmarked page number
   function getPage(){
       return pages[bookMarkedPage];
   }
   return getPage;
}

var myBook = book(),
    myPage = myBook.getPage();

Wenn Sie die Funktion book() ausführen, ordnen Sie Speicher im Stapel zu, in dem die Funktion ausgeführt werden soll. Da jedoch eine Funktion zurückgegeben wird, kann der Speicher nicht freigegeben werden, da die innere Funktion über den Kontext auf die Variablen zugreifen kann außerhalb davon, in diesem Fall "Seiten" und "bookMarkedPage".

Der effektive Aufruf von book() gibt einen Verweis auf einen Abschluß zurück, dh nicht nur eine Funktion, sondern einen Verweis auf das Buch und seinen Kontext, dh einen Verweis auf die Funktion getPage , Zustand von Seiten und bookMarkedPage Variablen.

Einige Punkte zu beachten:

Punkt 1: Das Bücherregal hat, genau wie der Funktionsstapel, nur begrenzten Platz. Verwenden Sie es also mit Bedacht.

Punkt 2: Überlegen Sie, ob Sie das gesamte Buch festhalten müssen, wenn Sie nur eine einzelne Seite nachverfolgen möchten. Sie können einen Teil des Speichers freigeben, indem Sie nicht alle Seiten im Buch speichern, wenn der Abschluss zurückgegeben wird.

Dies ist meine Perspektive von Closures. Hoffe, es hilft, und wenn jemand der Meinung ist, dass dies nicht korrekt ist, lassen Sie es mich bitte wissen, da ich sehr daran interessiert bin, noch mehr über Bereiche und Abschlüsse zu verstehen!

5
poushy

Auch ... Vielleicht sollten wir Ihrem 27-jährigen Freund eine kleine Pause machen, weil das gesamte Konzept der "Verschlüsse" wirklich - ist (!) ... Voodoo!

Damit meine ich: (a) Sie erwarten es nicht intuitiv ... UND ... (b) , wenn jemand das nimmt Zeit, es dir zu erklären, du erwartest sicher nicht, dass es funktioniert!

Intuition sagt dir, dass "das muss Unsinn sein ... sicher es muss zu einer Art Syntaxfehler führen oder so!" Wie um alles in der Welt (!) könnten Sie tatsächlich "eine Funktion aus der Mitte von" ziehen, wo immer es ist ", so dass Sie [noch!] Tatsächlich gelesen haben könnten/Schreibzugriff auf den Kontext von "wherever-it - was - at ?!"

Wenn Sie endlich erkennen, dass so etwas möglich ist , dann ... sicher ... wäre die After-the-fact-Reaktion von jedem: " whoa-aaa (!) ... kew-el-lll ... (!!!) "

Aber es wird eine "große kontraintuitive Hürde" geben, die es zu überwinden gilt. Intuition gibt Ihnen eine Menge absolut plausibler Erwartungen, dass so etwas "natürlich , absolut unsinnig und daher völlig unmöglich" wäre.

Wie ich schon sagte: "Es ist Voodoo."

4
Mike Robinson

Die einfachste, kürzeste und verständlichste Antwort:

Ein Closure ist ein Codeblock, in dem jede Zeile auf denselben Variablensatz mit denselben Variablennamen verweisen kann.

Wenn "dies" etwas anderes bedeutet als irgendwo anders, dann wissen Sie, dass es sich um zwei verschiedene Verschlüsse handelt.

4
Nick Manning

Closure kann aus privaten und öffentlichen Variablen oder Funktionen bestehen.

var ClusureDemo = function() {
    //privare variables
    var localVa1, localVa2;

    //private functions
    var setVaOne = function(newVa) {
        localVa1 = newVa;
    },
    setVaTwo = function(newVa) {
        localVa2 = newVa;
    },
    getVaOne = function() {
        return localVa1;
    },
    getVaTwo = function() {
        return localVa2;
    };

    return {
        //public variables and functions
        outVaOne : localVa1,
        outVaTwo : localVa2,
        setVaOne : setVaOne,
        setVaTwo : setVaTwo,
        getVaOne : getVaOne,
        getVaTwo : getVaTwo
    };
};

//Test Demo
var app = new ClusureDemo();
app.outVaOne = 'Hello Variable One';
app.outVaTwo = 'Hello Variable Two';
app.setVaOne(app.outVaOne);
app.setVaTwo(app.outVaTwo);

alert(app.getVaOne());
alert(app.getVaTwo());

Demo

2
Pao Im

Ein Abschluss ist eine Funktion, die Zugriff auf Informationen aus der Umgebung hat, in der er definiert wurde.

Für einige ist die Information Wert in der Umgebung zum Zeitpunkt der Erstellung. Für andere sind die Informationen die Variablen in der Umgebung zum Zeitpunkt der Erstellung.

Wenn die lexikalische Umgebung, auf die sich der Abschluss bezieht, zu einer Funktion gehört, die bereits beendet wurde, bleiben diese lexikalischen Variablen (im Fall eines Abschlusses, der sich auf die Variablen in der Umgebung bezieht) weiterhin für den Abschluss als Referenz vorhanden.

Als Abschluss kann ein Sonderfall globaler Variablen betrachtet werden - mit einer privaten Kopie, die nur für die Funktion erstellt wurde.

Oder Sie können sich eine Methode vorstellen, bei der die Umgebung eine bestimmte Instanz eines Objekts ist, dessen Eigenschaften die Variablen in der Umgebung sind.

Ersteres (Abschluss als Umgebung) ähnelt dem letzteren, wobei die Umgebungskopie eine Kontextvariable ist, die an jede Funktion im ersteren übergeben wird, und die Instanzvariablen im letzteren eine Kontextvariable bilden.

Ein Closure ist also eine Möglichkeit, eine Funktion aufzurufen, ohne den Kontext explizit als Parameter oder als Objekt in einem Methodenaufruf angeben zu müssen.

var closure = createclosure(varForClosure);
closure(param1);  // closure has access to whatever createclosure gave it access to,
                  // including the parameter storing varForClosure.

vs

var contextvar = varForClosure; // use a struct for storing more than one..
contextclosure(contextvar, param1);

vs

var contextobj = new contextclass(varForClosure);
contextobj->objclosure(param1);

Für wartbaren Code empfehle ich den objektorientierten Weg. Für eine schnelle und einfache Abwicklung von Aufgaben (z. B. das Erstellen eines Rückrufs) kann ein Abschluss jedoch natürlich und klarer werden, insbesondere im Zusammenhang mit lamda oder anonymen Funktionen.

2
Gerard ONeill

Ein Abschluss ist einfach, wenn eine Funktion Zugriff auf ihren externen Bereich hat, auch wenn die Ausführung der Funktion des Bereichs abgeschlossen ist. Beispiel:

function multiplier(n) {
    function multiply(x) {
          return n*x;
    }
    return mutliply;
}

var 10xmultiplier = multiplier(10);
var x = 10xmultiplier(5); // x= 50

wir können sehen, dass die innere Funktion multiplizieren auch nach Beendigung der Ausführung des Multiplikators noch Zugriff auf den Wert von x erhält, der in diesem Beispiel 10 ist.

Eine sehr häufige Verwendung von Closures ist das Currying (das gleiche Beispiel oben), bei dem wir unsere Funktion schrittweise mit Parametern würzen, anstatt alle Argumente auf einmal anzugeben.

Wir können dies erreichen, weil Javascript (zusätzlich zum prototypischen OOP) ermöglicht, auf funktionale Weise zu programmieren, wobei Funktionen höherer Ordnung andere Funktionen als Argumente annehmen können (erste Klassenfunktionen). funktionale Programmierung in Wikipedia

Ich empfehle Ihnen dringend, dieses Buch von Kyle Simpson zu lesen: 2 Ein Teil der Buchreihe ist Verschlüssen gewidmet und heißt Scope and Closures. du kennst js nicht: freies lesen auf github

2
zak.http