7619 Stimmen

Wie funktionieren die JavaScript-Schließungen?

Wie würden Sie JavaScript-Schließungen jemandem erklären, der zwar die Konzepte kennt, aus denen sie bestehen (z. B. Funktionen, Variablen und Ähnliches), aber die Schließungen selbst nicht versteht?

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

239voto

Magne Punkte 15365

Die ursprüngliche Frage enthielt ein Zitat:

Wenn Sie es einem Sechsjährigen nicht erklären können, haben Sie es selbst nicht verstanden.

So würde ich versuchen, es einem Sechsjährigen zu erklären:

Sie wissen doch, dass Erwachsene ein Haus besitzen können und es ihr Zuhause nennen? Wenn eine Mutter ein Kind bekommt, gehört dem Kind eigentlich nichts, oder? Aber seine Eltern besitzen ein Haus, und wenn jemand das Kind fragt: "Wo ist dein Zuhause?", kann es antworten: "In diesem Haus!", und auf das Haus seiner Eltern zeigen. Ein "Abschluss" ist die Fähigkeit des Kindes, immer (auch im Ausland) sagen zu können, dass es ein Haus hat, obwohl es in Wirklichkeit die Eltern sind, denen das Haus gehört.

226voto

Matt Punkte 746

Die Verschlüsse sind einfach:

Das folgende einfache Beispiel deckt alle wichtigen Punkte von JavaScript-Schließungen ab. *  

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 springende Punkt: Jeder Aufruf von make_calculator erstellt eine neue lokale Variable n die weiterhin von diesem Rechner verwendet werden kann add y multiply funktioniert noch lange nach make_calculator zurück.

_Wenn Sie mit Stack Frames vertraut sind, erscheinen Ihnen diese Rechner seltsam: Wie können sie ständig auf nmake_calculator zurück? Die Antwort besteht darin, sich vorzustellen, dass JavaScript keine "Stack-Frames", sondern "Heap-Frames" verwendet, die auch nach der Rückkehr des Funktionsaufrufs, der sie erzeugt hat, bestehen bleiben können._

Innere Funktionen wie add y multiply die auf Variablen zugreifen, die in einer äußeren Funktion deklariert sind ** werden genannt Verschlüsse .

Das ist so ziemlich alles, was es an Schließungen gibt.


* Sie deckt zum Beispiel alle Punkte des Artikels "Closures for Dummies" ab, der in eine weitere Antwort mit Ausnahme von Beispiel 6, das einfach zeigt, dass Variablen verwendet werden können, bevor sie deklariert werden, was zwar gut zu wissen ist, aber nichts mit Schließungen zu tun hat. Es deckt auch alle Punkte in die akzeptierte Antwort Mit Ausnahme der Punkte (1), dass Funktionen ihre Argumente in lokale Variablen (die benannten Funktionsargumente) kopieren, und (2) dass das Kopieren von Zahlen eine neue Zahl erzeugt, das Kopieren einer Objektreferenz jedoch eine weitere Referenz auf dasselbe Objekt ergibt. Auch das ist gut zu wissen, hat aber wiederum nichts mit Schließungen zu tun. Es ist auch sehr ähnlich zu dem Beispiel in diese Antwort aber ein bisschen kürzer und weniger abstrakt. Er umfasst nicht den Punkt der diese Antwort o dieser Kommentar ist, dass JavaScript es schwierig macht, die actual Wert einer Schleifenvariablen in Ihre innere Funktion: Der Schritt des "Einfügens" kann nur mit einer Hilfsfunktion erfolgen, die Ihre innere Funktion umschließt und bei jeder Schleifeniteration aufgerufen wird. (Streng genommen greift die innere Funktion auf die Kopie der Variablen in der Hilfsfunktion zu und nicht auf etwas, das eingesteckt wurde). Auch dies ist sehr nützlich bei der Erstellung von Closures, aber nicht Teil dessen, was ein Closure ist oder wie es funktioniert. Zusätzliche Verwirrung entsteht dadurch, dass Closures in funktionalen Sprachen wie ML anders funktionieren, wo Variablen an Werte und nicht an Speicherplatz gebunden sind. Das sorgt für einen ständigen Strom von Leuten, die Closures auf eine Art und Weise verstehen (nämlich das "Einstecken"), die für JavaScript einfach falsch ist, wo Variablen immer an Speicherplatz und nie an Werte gebunden sind.

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

214voto

Chris S Punkte 63542

Können Sie einem 5-Jährigen erklären, wie man Schließungen durchführt?*

Ich denke immer noch Die 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

190voto

Chev Punkte 56446

Ich lerne am besten durch GUT/SCHLECHT-Vergleiche. Ich mag es, funktionierenden Code gefolgt von nicht funktionierendem Code zu sehen, auf den man wahrscheinlich stoßen wird. Ich stelle zusammen ein jsFiddle die einen Vergleich anstellt und versucht, die Unterschiede auf die einfachsten Erklärungen zu reduzieren, die mir einfallen.

Schließungen 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]());
}
  • In dem obigen Code createClosure(n) wird in jeder Iteration der Schleife aufgerufen. Beachten Sie, dass ich die Variable n um hervorzuheben, dass es sich um eine neu Variable, die in einem neuen Funktionsbereich erstellt wurde und nicht dieselbe Variable ist wie index die an den äußeren Bereich gebunden ist.

  • Dadurch wird ein neuer Bereich geschaffen und n ist an diesen Bereich gebunden; das 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 Bereichs n ist an den Wert gebunden, den es hatte, als createClosure(n) aufgerufen wurde, so dass die verschachtelte Funktion, die zurückgegeben wird, immer den Wert von n die sie hatte, als createClosure(n) aufgerufen wurde.

Falsch durchgeführte Schließungen:

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 createClosureArray() Funktion und die Funktion gibt nun einfach das fertige Array zurück, was auf den ersten Blick intuitiver erscheint.

  • Was vielleicht nicht offensichtlich ist, ist die Tatsache, dass createClosureArray() nur einmal aufgerufen wird, wird nur ein Bereich für diese Funktion erstellt und nicht einer für jede Iteration der Schleife.

  • Innerhalb dieser Funktion wird eine Variable namens index definiert ist. Die Schleife läuft und fügt dem Array Funktionen hinzu, die Folgendes zurückgeben index . Beachten Sie, dass index wird im Rahmen der createClosureArray Funktion, die nur ein einziges Mal aufgerufen wird.

  • Da es nur einen Bereich innerhalb der createClosureArray() Funktion, index ist nur an einen Wert innerhalb dieses Bereichs gebunden. Mit anderen Worten: Jedes Mal, wenn die Schleife den Wert von index wird sie für alles geändert, was innerhalb dieses Bereichs darauf verweist.

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

  • Nachdem die Schleife beendet ist und index geändert wurde, war der Endwert 10, daher gibt jede Funktion, die dem Array hinzugefügt wird, den Wert der einzelnen index die jetzt auf 10 gesetzt ist.

Ergebnis

VERSCHLÜSSE RICHTIG GEMACHT
n = 0
n = 1
n = 2
n = 3
n = 4
n = 5
n = 6
n = 7
n = 8
n = 9

FALSCH GEMACHTE VERSCHLÜSSE
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10

171voto

mykhal Punkte 18078

Wikipedia über Schließungen :

In der Informatik ist eine Closure eine Funktion zusammen mit einer referenzierenden Umgebung für die nichtlokalen Namen (freie Variablen) dieser Funktion.

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

Seit Umfangsbestimmende Konstruktion in JavaScript ist eine Funktion und nicht ein Codeblock wie in vielen anderen Sprachen, was wir normalerweise meinen mit Verschluss in JavaScript ist eine Funktion, die mit nichtlokalen Variablen arbeitet, die in einer bereits ausgeführten umgebenden Funktion definiert sind .

Closures werden oft verwendet, um Funktionen mit versteckten privaten Daten zu erstellen (aber das ist 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

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

CodeJaeger.com

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

Powered by:

X