Was ist eine Schließung?


155

Hin und wieder sehe ich, dass "Abschlüsse" erwähnt werden, und ich habe versucht, nachzuschlagen, aber Wiki gibt keine Erklärung, die ich verstehe. Könnte mir hier jemand helfen?


Wenn Sie wissen , Java / C # hoffen , dass diese Verbindung wird Hilfe- http://www.developerfusion.com/article/8251/the-beauty-of-closures/
Gulshan

1
Verschlüsse sind schwer zu verstehen. Sie sollten versuchen, auf alle Links im ersten Satz dieses Wikipedia-Artikels zu klicken und diese Artikel zuerst zu verstehen.
Zach


3
Was ist der grundlegende Unterschied zwischen einem Abschluss und einer Klasse? Okay, eine Klasse mit nur einer öffentlichen Methode.
biziclop

5
@biziclop: Sie könnten einen Abschluss mit einer Klasse emulieren (das müssen Java-Entwickler tun). In der Regel sind sie jedoch etwas weniger ausführlich zu erstellen und Sie müssen nicht manuell verwalten, um was es sich handelt. (Die Hardcore-Hörer stellen eine ähnliche Frage, kommen aber wahrscheinlich zu dem anderen Schluss - dass OO-Unterstützung auf Sprachniveau nicht erforderlich ist, wenn Sie Abschlüsse haben).

Antworten:


141

(Haftungsausschluss: Dies ist eine grundlegende Erklärung. Was die Definition angeht, vereinfache ich ein wenig.)

Die einfachste Art, sich einen Abschluss vorzustellen, ist eine Funktion, die als Variable gespeichert werden kann (als "erstklassige Funktion" bezeichnet) und die über die besondere Fähigkeit verfügt, auf andere Variablen zuzugreifen, die lokal zu dem Bereich gehören, in dem sie erstellt wurden.

Beispiel (JavaScript):

var setKeyPress = function(callback) {
    document.onkeypress = callback;
};

var initialize = function() {
    var black = false;

    document.onclick = function() {
        black = !black;
        document.body.style.backgroundColor = black ? "#000000" : "transparent";
    }

    var displayValOfBlack = function() {
        alert(black);
    }

    setKeyPress(displayValOfBlack);
};

initialize();

Die Funktionen 1 zugewiesen document.onclickund displayValOfBlacksind Verschlüsse. Sie können sehen, dass beide auf die boolesche Variable verweisen black, diese Variable jedoch außerhalb der Funktion zugewiesen wird. Da blackist auf den Umfang lokale wobei die Funktion definiert wurde , wird der Zeiger auf diese Variable erhalten.

Wenn Sie dies in eine HTML-Seite einfügen:

  1. Klicken Sie, um zu Schwarz zu wechseln
  2. Drücke [Enter] um "true" zu sehen
  3. Klicken Sie erneut, wechselt zurück zu Weiß
  4. Drücken Sie die Eingabetaste, um "false" zu sehen.

Dies zeigt, dass beide Zugriff auf dasselbe Objekt haben blackund zum Speichern des Status ohne Wrapper-Objekt verwendet werden können.

Der Aufruf von setKeyPresssoll demonstrieren, wie eine Funktion genau wie eine Variable übergeben werden kann. Der Gültigkeitsbereich , der im Abschluß erhalten bleibt, ist immer noch derjenige, in dem die Funktion definiert wurde.

Closures werden häufig als Ereignishandler verwendet, insbesondere in JavaScript und ActionScript. Durch die ordnungsgemäße Verwendung von Closures können Sie Variablen implizit an Ereignishandler binden, ohne einen Objektwrapper erstellen zu müssen. Eine unachtsame Verwendung führt jedoch zu Speicherverlusten (z. B. wenn ein nicht verwendeter, aber erhaltener Ereignishandler das einzige ist, an dem große Objekte im Speicher, insbesondere DOM-Objekte, festgehalten werden, wodurch die Garbage Collection verhindert wird).


1: Eigentlich sind alle Funktionen in JavaScript Abschlüsse.


3
Als ich Ihre Antwort las, fühlte ich, wie eine Glühbirne in meinem Kopf aufleuchtete. Sehr geschätzt! :)
Jay

1
Da blackin einer Funktion deklariert ist, würde das nicht zerstört werden, wenn sich der Stapel abwickelt ...?
Gablin

1
@gablin, das ist das Einzigartige an Sprachen, die Abschlüsse haben. Alle Sprachen mit Garbage Collection funktionieren auf die gleiche Weise - wenn keine Referenzen mehr auf ein Objekt gespeichert werden, kann es zerstört werden. Immer wenn eine Funktion in JS erstellt wird, ist der lokale Bereich an diese Funktion gebunden, bis diese Funktion zerstört wird.
Nicole

2
@gablin, das ist eine gute Frage. Ich glaube nicht, dass sie nicht & mdash; Aber ich habe die Garbage Collection erst seit dem, was JS verwendet, aufgegriffen, und darauf haben Sie sich anscheinend bezogen, als Sie sagten: "Since blackwird in einer Funktion deklariert, würde das nicht zerstört werden." Denken Sie auch daran, dass das Objekt erhalten bleibt, wenn Sie ein Objekt in einer Funktion deklarieren und es dann einer Variablen zuweisen, die sich an einer anderen Stelle befindet, da es andere Verweise darauf gibt.
Nicole

1
Objective-C (und C unter Clang) unterstützen Blöcke, die im Wesentlichen Abschlüsse sind, ohne Garbage Collection. Es erfordert Laufzeitunterstützung und einige manuelle Eingriffe in die Speicherverwaltung.
Quixoto

68

Ein Verschluss ist im Grunde nur eine andere Sichtweise auf ein Objekt. Ein Objekt besteht aus Daten, an die eine oder mehrere Funktionen gebunden sind. Ein Abschluß ist eine Funktion, an die eine oder mehrere Variablen gebunden sind. Die beiden sind im Grunde genommen identisch, zumindest auf Implementierungsebene. Der wirkliche Unterschied liegt darin, woher sie kommen.

Bei der objektorientierten Programmierung deklarieren Sie eine Objektklasse, indem Sie ihre Elementvariablen und ihre Methoden (Elementfunktionen) im Voraus definieren und dann Instanzen dieser Klasse erstellen. Jede Instanz wird mit einer Kopie der Mitgliedsdaten geliefert, die vom Konstruktor initialisiert wurden. Sie haben dann eine Variable eines Objekttyps und übergeben sie als Datenelement, da der Fokus auf ihrer Art als Daten liegt.

In einem Closure wird das Objekt hingegen nicht wie eine Objektklasse im Voraus definiert oder durch einen Konstruktoraufruf in Ihrem Code instanziiert. Stattdessen schreiben Sie den Abschluss als Funktion in eine andere Funktion. Der Abschluss kann auf jede lokale Variable der äußeren Funktion verweisen, und der Compiler erkennt dies und verschiebt diese Variablen aus dem Stapelbereich der äußeren Funktion in die Wimmelbilddeklaration des Abschlusses. Sie haben dann eine Variable eines Verschlusstyps, und obwohl es sich im Grunde um ein Objekt unter der Haube handelt, geben Sie es als Funktionsreferenz weiter, da der Fokus auf seiner Art als Funktion liegt.


3
+1: Gute Antwort. Sie können einen Abschluss als ein Objekt mit nur einer Methode und ein beliebiges Objekt als eine Sammlung von Abschlüssen über einige allgemeine zugrunde liegende Daten (die Mitgliedsvariablen des Objekts) anzeigen. Ich denke, diese beiden Ansichten sind ziemlich symmetrisch.
Giorgio

3
Sehr gute Antwort. Es erklärt tatsächlich die Einsicht der Schließung.
RoboAlex

1
@Mason Wheeler: Wo werden Schließungsdaten gespeichert? Im Stapel wie eine Funktion? Oder auf einem Haufen wie ein Gegenstand?
RoboAlex

1
@RoboAlex: Im Heap, weil es ein Objekt ist, das wie eine Funktion aussieht .
Mason Wheeler

1
@RoboAlex: Wo ein Abschluss und seine erfassten Daten gespeichert werden, hängt von der Implementierung ab. In C ++ kann es im Heap oder auf dem Stack gespeichert werden.
Giorgio,

29

Der Begriff Closure ergibt sich aus der Tatsache, dass ein Codeteil (Block, Funktion) freie Variablen haben kann, die durch die Umgebung, in der der Codeblock definiert ist , geschlossen (dh an einen Wert gebunden) werden.

Nehmen Sie zum Beispiel die Scala-Funktionsdefinition:

def addConstant(v: Int): Int = v + k

In der Funktionskörper sind zwei Namen (Variablen) vund kangibt zwei ganzzahlige Werte. Der Name vist gebunden, weil er als Argument der Funktion deklariert ist addConstant(wenn wir uns die Funktionsdeklaration ansehen, wissen wir, dass vdieser beim Aufruf der Funktion ein Wert zugewiesen wird). Der Name kist für die Funktion frei, addConstantda die Funktion keinen Hinweis darauf enthält, an welchen Wert k(und wie) dieser Wert gebunden ist.

Um einen Anruf wie folgt zu bewerten:

val n = addConstant(10)

wir müssen keinen Wert zuweisen , was nur passieren kann, wenn der Name kin dem Kontext definiert ist, in dem er addConstantdefiniert ist. Zum Beispiel:

def increaseAll(values: List[Int]): List[Int] =
{
  val k = 2

  def addConstant(v: Int): Int = v + k

  values.map(addConstant)
}

Nachdem wir nun addConstantin einem Kontext kdefiniert haben, in dem dies definiert ist, addConstantwurde es zu einem Abschluss, da alle seine freien Variablen jetzt geschlossen sind (an einen Wert gebunden sind): Sie addConstantkönnen aufgerufen und herumgereicht werden, als ob es eine Funktion wäre. Beachten Sie, dass die freie Variable kbeim Definieren des Abschlusses an einen Wert gebunden ist , während die Argumentvariable vbeim Aufrufen des Abschlusses gebunden ist .

Ein Closure ist also im Grunde genommen ein Funktions- oder Codeblock, der über seine freien Variablen auf nicht lokale Werte zugreifen kann, nachdem diese durch den Kontext gebunden wurden.

Wenn Sie einen Verschluss nur einmal verwenden, können Sie ihn in vielen Sprachen anonymisieren , z

def increaseAll(values: List[Int]): List[Int] =
{
  val k = 2

  values.map(v => v + k)
}

Beachten Sie, dass eine Funktion ohne freie Variablen ein Sonderfall eines Abschlusses ist (mit einem leeren Satz freier Variablen). Analog ist eine anonyme Funktion ein Sonderfall eines anonymen Abschlusses , dh eine anonyme Funktion ist ein anonymer Abschluss ohne freie Variablen.


Dies passt gut zu geschlossenen und offenen Formeln in der Logik. Danke für deine Antwort.
RainDoctor

@RainDoctor: Freie Variablen werden in logischen Formeln und in Lambda-Ausdrücken auf ähnliche Weise definiert: Das Lambda in einem Lambda-Ausdruck funktioniert wie ein Quantifizierer in logischen Formeln für freie / gebundene Variablen.
Giorgio

9

Eine einfache Erklärung in JavaScript:

var closure_example = function() {
    var closure = 0;
    // after first iteration the value will not be erased from the memory
    // because it is bound with the returned alertValue function.
    return {
        alertValue : function() {
            closure++;
            alert(closure);
        }
    };
};
closure_example();

alert(closure)verwendet den zuvor erstellten Wert von closure. Der alertValueNamespace der zurückgegebenen Funktion wird mit dem Namespace verbunden, in dem sich die closureVariable befindet. Wenn Sie die gesamte Funktion löschen, wird der Wert der closureVariablen gelöscht, aber bis dahin kann die alertValueFunktion den Wert der Variablen immer lesen / schreiben closure.

Wenn Sie diesen Code ausführen, weist die erste Iteration der closureVariablen den Wert 0 zu und schreibt die Funktion folgendermaßen um:

var closure_example = function(){
    alertValue : function(){
        closure++;
        alert(closure);
    }       
}

Und da alertValuedie lokale Variable closurezum Ausführen der Funktion benötigt wird, bindet sie sich an den Wert der zuvor zugewiesenen lokalen Variablen closure.

Und jetzt wird jedes Mal, wenn Sie die closure_exampleFunktion aufrufen , der inkrementierte Wert der closureVariablen ausgegeben, da diese alert(closure)gebunden ist.

closure_example.alertValue()//alerts value 1 
closure_example.alertValue()//alerts value 2 
closure_example.alertValue()//alerts value 3
//etc. 

danke, ich habe den code nicht getestet =) jetzt scheint alles in ordnung zu sein.
Muha

5

Ein "Abschluß" ist im wesentlichen ein lokaler Zustand und ein Code, die zu einem Paket zusammengefaßt sind. Typischerweise stammt der lokale Zustand aus einem umgebenden (lexikalischen) Bereich, und der Code ist (im Wesentlichen) eine innere Funktion, die dann nach außen zurückgegeben wird. Der Abschluss ist dann eine Kombination der erfassten Variablen, die die innere Funktion sieht, und des Codes der inneren Funktion.

Es ist eines dieser Dinge, die leider ein bisschen schwer zu erklären sind, weil sie unbekannt sind.

Eine Analogie, die ich in der Vergangenheit erfolgreich verwendet habe, war: "Stellen Sie sich vor, wir haben etwas, das wir" das Buch "nennen, im Raumabschluss," das Buch "ist das Exemplar dort, in der Ecke, von TAOCP, aber auf dem Tischabschluss , es ist die Kopie eines Dresdner Aktenbuchs. Je nachdem, in welchem ​​Abschluss Sie sich befinden, führt der Code "Gib mir das Buch" zu unterschiedlichen Ereignissen. "


Sie haben dies vergessen: en.wikipedia.org/wiki/Closure_(computer_programming) in Ihrer Antwort.
S.Lott

3
Nein, ich habe mich bewusst dafür entschieden, diese Seite nicht zu schließen.
Vatine

"Zustand und Funktion.": Kann eine C-Funktion mit einer staticlokalen Variablen als Abschluß betrachtet werden? Beziehen Schließungen in Haskell den Staat mit ein?
Giorgio

2
@Giorgio Closures in Haskell schließen (glaube ich) die Argumente in dem lexikalischen Bereich ab, in dem sie definiert sind, also würde ich "Ja" sagen (obwohl ich mit Haskell bestenfalls nicht vertraut bin). Eine Wechselstromfunktion mit einer statischen Variablen ist bestenfalls ein sehr begrenzter Abschluss (Sie möchten wirklich in der Lage sein, mehrere Abschlüsse aus einer einzigen Funktion zu erstellen, mit einer staticlokalen Variablen haben Sie genau eine).
Vatine

Ich habe diese Frage absichtlich gestellt, weil ich denke, dass eine C-Funktion mit einer statischen Variablen keine Schließung ist: Die statische Variable ist lokal definiert und nur innerhalb der Schließung bekannt, sie greift nicht auf die Umgebung zu. Ich bin mir auch nicht 100% sicher, aber ich würde Ihre Aussage andersherum formulieren: Sie verwenden den Schließungsmechanismus, um verschiedene Funktionen zu erstellen (eine Funktion ist eine Schließungsdefinition + eine Bindung für ihre freien Variablen).
Giorgio

5

Es ist schwer zu definieren, was ein Abschluss ist, ohne den Begriff „Staat“ zu definieren.

Grundsätzlich passiert in einer Sprache mit vollständigem lexikalischem Geltungsbereich, in der Funktionen als erstklassige Werte behandelt werden, etwas Besonderes. Wenn ich etwas machen würde wie:

function foo(x)
return x
end

x = foo

Die Variable xverweist nicht nur auf function foo()den Status, sondern auch auf den Status, fooden sie beim letzten Zurückgeben belassen hat. Die wahre Magie entsteht, wenn fooandere Funktionen in ihrem Geltungsbereich näher definiert sind. Es ist wie eine eigene Mini-Umgebung (genau wie "normal" definieren wir Funktionen in einer globalen Umgebung).

Funktional kann es viele der gleichen Probleme lösen wie das Schlüsselwort 'static' in C ++ (C?), Das den Status einer lokalen Variablen bei mehreren Funktionsaufrufen beibehält. Es ist jedoch eher so, als würde man dasselbe Prinzip (statische Variable) auf eine Funktion anwenden, da Funktionen erstklassige Werte sind. Closure fügt Unterstützung für das Speichern des gesamten Funktionszustands hinzu (hat nichts mit den statischen Funktionen von C ++ zu tun).

Das Behandeln von Funktionen als erstklassige Werte und das Hinzufügen von Unterstützung für Abschlüsse bedeutet auch, dass Sie mehr als eine Instanz derselben Funktion im Speicher haben können (ähnlich wie bei Klassen). Dies bedeutet, dass Sie denselben Code wiederverwenden können, ohne den Funktionsstatus zurücksetzen zu müssen, wie dies erforderlich ist, wenn Sie mit statischen C ++ - Variablen in einer Funktion arbeiten (kann dies falsch sein?).

Hier finden Sie einige Tests zur Unterstützung der Schließung von Lua.

--Closure testing
--By Trae Barlow
--

function myclosure()
    print(pvalue)--nil
    local pvalue = pvalue or 10
    return function()
        pvalue = pvalue + 10 --20, 31, 42, 53(53 never printed)
        print(pvalue)
        pvalue = pvalue + 1 --21, 32, 43(pvalue state saved through multiple calls)
        return pvalue
    end
end

x = myclosure() --x now references anonymous function inside myclosure()

x()--nil, 20
x() --21, 31
x() --32, 42
    --43, 53 -- if we iterated x() again

Ergebnisse:

nil
20
31
42

Es kann schwierig werden, und es variiert wahrscheinlich von Sprache zu Sprache, aber in Lua scheint es, dass der Status jedes Mal zurückgesetzt wird, wenn eine Funktion ausgeführt wird. Ich sage das, weil die Ergebnisse aus dem obigen Code anders wären, wenn wir myclosuredirekt auf die Funktion / den Zustand zugreifen würden (anstatt über die anonyme Funktion, die sie zurückgibt), da pvaluesie auf 10 zurückgesetzt würden. aber wenn wir über x (die anonyme Funktion) auf den Status von myclosure zugreifen, können Sie sehen, dass pvaluedas lebendig ist und sich irgendwo im Speicher befindet. Ich vermute, es steckt ein bisschen mehr dahinter, vielleicht kann jemand die Art der Implementierung besser erklären.

PS: Ich kenne kein bisschen C ++ 11 (anders als in früheren Versionen). Beachten Sie also, dass dies kein Vergleich zwischen Closures in C ++ 11 und Lua ist. Alle Linien, die von Lua nach C ++ gezogen werden, sind Ähnlichkeiten, da statische Variablen und Abschlüsse nicht zu 100% gleich sind. auch wenn sie manchmal verwendet werden, um ähnliche Probleme zu lösen.

Ich bin mir nicht sicher, ob im obigen Codebeispiel die anonyme Funktion oder die Funktion höherer Ordnung als Abschluss betrachtet wird.


4

Ein Abschluss ist eine Funktion mit folgendem Status:

In Perl erstellen Sie Abschlüsse wie folgt:

#!/usr/bin/perl

# This function creates a closure.
sub getHelloPrint
{
    # Bind state for the function we are returning.
    my ($first) = @_;a

    # The function returned will have access to the variable $first
    return sub { my ($second) = @_; print  "$first $second\n"; };
}

my $hw = getHelloPrint("Hello");
my $gw = getHelloPrint("Goodby");

&$hw("World"); // Print Hello World
&$gw("World"); // PRint Goodby World

Wenn wir uns die neue Funktionalität von C ++ ansehen.
Außerdem können Sie den aktuellen Status an das Objekt binden:

#include <string>
#include <iostream>
#include <functional>


std::function<void(std::string const&)> getLambda(std::string const& first)
{
    // Here we bind `first` to the function
    // The second parameter will be passed when we call the function
    return [first](std::string const& second) -> void
    {   std::cout << first << " " << second << "\n";
    };
}

int main(int argc, char* argv[])
{
    auto hw = getLambda("Hello");
    auto gw = getLambda("GoodBye");

    hw("World");
    gw("World");
}

2

Betrachten wir eine einfache Funktion:

function f1(x) {
    // ... something
}

Diese Funktion wird als Top-Level-Funktion bezeichnet, da sie in keiner anderen Funktion verschachtelt ist. Jede JavaScript-Funktion verknüpft sich mit einer Liste von Objekten, die als "Scope Chain" bezeichnet werden . Diese Scope-Kette ist eine geordnete Liste von Objekten. Jedes dieser Objekte definiert einige Variablen.

In Funktionen der obersten Ebene besteht die Scope-Kette aus einem einzelnen Objekt, dem globalen Objekt. Die f1obige Funktion verfügt beispielsweise über eine Bereichskette, in der sich ein einzelnes Objekt befindet, das alle globalen Variablen definiert. (Beachten Sie, dass der Begriff "Objekt" hier kein JavaScript-Objekt bedeutet. Es handelt sich lediglich um ein implementierungsdefiniertes Objekt, das als Variablencontainer fungiert und in dem JavaScript Variablen "nachschlagen" kann.)

Wenn diese Funktion aufgerufen wird, erstellt JavaScript ein sogenanntes "Aktivierungsobjekt" und setzt es an die Spitze der Scope-Kette. Dieses Objekt enthält alle lokalen Variablen (zum Beispiel xhier). Daher haben wir jetzt zwei Objekte in der Scope-Kette: Das erste ist das Aktivierungsobjekt und darunter das globale Objekt.

Beachten Sie sehr sorgfältig, dass die beiden Objekte zu unterschiedlichen Zeiten in die Scope-Kette aufgenommen werden. Das globale Objekt wird abgelegt, wenn die Funktion definiert ist (dh wenn JavaScript die Funktion analysiert und das Funktionsobjekt erstellt hat), und das Aktivierungsobjekt wird beim Aufrufen der Funktion eingegeben.

Nun wissen wir also:

  • Jeder Funktion ist eine Scope-Kette zugeordnet
  • Wenn die Funktion definiert ist (wenn das Funktionsobjekt erstellt wird), speichert JavaScript eine Bereichskette mit dieser Funktion
  • Bei Funktionen der obersten Ebene enthält die Scope-Kette zum Zeitpunkt der Funktionsdefinition nur das globale Objekt und fügt zum Zeitpunkt des Aufrufs ein zusätzliches Aktivierungsobjekt hinzu

Interessant wird die Situation, wenn es sich um verschachtelte Funktionen handelt. Also, lasst uns einen erstellen:

function f1(x) {

    function f2(y) {
        // ... something
    }

}

Wenn f1definiert wird, erhalten wir eine Scope-Kette, die nur das globale Objekt enthält.

Jetzt, wenn es f1aufgerufen wird, erhält die Scope-Kette von f1das Aktivierungsobjekt. Dieses Aktivierungsobjekt enthält die Variable xund die Variable, f2die eine Funktion ist. Und beachten Sie, f2dass definiert wird. Daher speichert JavaScript zu diesem Zeitpunkt auch eine neue Bereichskette für f2. Die für diese innere Funktion gespeicherte Scope-Kette ist die aktuell gültige Scope-Kette. Die derzeitige Gültigkeitskette ist die von f1. Daher ist f2die Scope Chain f1die aktuelle Scope Chain, die das Aktivierungsobjekt von f1und das globale Objekt enthält.

Wenn f2aufgerufen wird, erhält es ein eigenes Aktivierungsobjekt y, das der Gültigkeitsbereichskette hinzugefügt wurde, die bereits das Aktivierungsobjekt von f1und das globale Objekt enthält.

Wenn eine andere verschachtelte Funktion definiert wäre f2, würde ihre Gültigkeitsbereichskette zum Definitionszeitpunkt drei Objekte (2 Aktivierungsobjekte von zwei äußeren Funktionen und das globale Objekt) und 4 zum Aufrufzeitpunkt enthalten.

Jetzt verstehen wir, wie die Scope-Kette funktioniert, haben aber noch nicht über Schließungen gesprochen.

Die Kombination eines Funktionsobjekts und eines Gültigkeitsbereichs (eine Reihe von Variablenbindungen), in denen die Variablen der Funktion aufgelöst werden, wird in der Informatikliteratur als Closure bezeichnet - JavaScript, das definitive Handbuch von David Flanagan

Die meisten Funktionen werden mit derselben Bereichskette aufgerufen, die bei der Definition der Funktion wirksam war, und es spielt keine Rolle, ob es sich um einen Abschluss handelt. Abschlüsse werden interessant, wenn sie in einer anderen Gültigkeitsbereichskette aufgerufen werden als die, die zum Zeitpunkt ihrer Definition gültig war. Dies geschieht am häufigsten , wenn ein verschachtelten Funktion Objekt wird zurückgegeben von der Funktion , in dem sie festgelegt wurde.

Wenn die Funktion zurückgegeben wird, wird dieses Aktivierungsobjekt aus der Bereichskette entfernt. Wenn es keine verschachtelten Funktionen gibt, gibt es keine Referenzen mehr auf das Aktivierungsobjekt und es wird Müll gesammelt. Wenn verschachtelte Funktionen definiert wurden, hat jede dieser Funktionen einen Verweis auf die Bereichskette, und diese Bereichskette bezieht sich auf das Aktivierungsobjekt.

Wenn diese verschachtelten Funktionsobjekte jedoch in ihrer äußeren Funktion verbleiben, werden sie selbst zusammen mit dem Aktivierungsobjekt, auf das sie sich bezogen haben, mit Müll gesammelt. Wenn die Funktion jedoch eine verschachtelte Funktion definiert und diese zurückgibt oder in einer Eigenschaft speichert, gibt es einen externen Verweis auf die verschachtelte Funktion. Es wird kein Müll gesammelt und das Aktivierungsobjekt, auf das es verweist, wird auch kein Müll gesammelt.

In unserem obigen Beispiel kehren wir nicht f2von zurück f1. Wenn also ein Aufruf von " return" erfolgt f1, wird sein Aktivierungsobjekt aus seiner Gültigkeitsbereichskette entfernt und Garbage gesammelt. Aber wenn wir so etwas hätten:

function f1(x) {

    function f2(y) {
        // ... something
    }

    return f2;
}

Hier hat die Rückgabe f2eine Bereichskette, die das Aktivierungsobjekt von enthält f1, und daher wird kein Müll gesammelt. Wenn wir an dieser Stelle anrufen f2, kann es auf die f1Variable zugreifen , xauch wenn wir nicht da sind f1.

Daher können wir sehen, dass eine Funktion ihre Gültigkeitskette bei sich behält, und mit der Gültigkeitskette kommen alle Aktivierungsobjekte der äußeren Funktionen. Dies ist das Wesen der Schließung. Wir sagen, dass Funktionen in JavaScript "lexikalisch" sind , was bedeutet, dass sie den Bereich speichern, der aktiv war, als sie definiert wurden, und nicht den Bereich, der aktiv war, als sie aufgerufen wurden.

Es gibt eine Reihe leistungsfähiger Programmiertechniken, die das Annähern von privaten Variablen, ereignisgesteuerte Programmierung, Teilanwendung usw. umfassen.

Beachten Sie auch, dass all dies für alle Sprachen gilt, die Closures unterstützen. Zum Beispiel PHP (5.3+), Python, Ruby usw.


-1

Ein Closure ist eine Compiler-Optimierung (alias syntaktischer Zucker?). Einige Leute haben dies auch als das Objekt des Armen bezeichnet .

Siehe die Antwort von Eric Lippert : (Auszug unten)

Der Compiler generiert Code wie folgt:

private class Locals
{
  public int count;
  public void Anonymous()
  {
    this.count++;
  }
}

public Action Counter()
{
  Locals locals = new Locals();
  locals.count = 0;
  Action counter = new Action(locals.Anonymous);
  return counter;
}

Sinn ergeben?
Sie haben auch nach Vergleichen gefragt. Sowohl VB als auch JScript erstellen Abschlüsse auf die gleiche Art und Weise.


Diese Antwort ist ein CW, weil ich für Erics großartige Antwort keine Punkte verdiene. Bitte stimmen Sie es nach Belieben ab. HTH
goodguys_activate

3
-1: Ihre Erklärung ist zu root in C #. Verschluss wird in vielen Sprachen verwendet und ist in diesen Sprachen viel mehr als syntaktischer Zucker und umfasst sowohl die Funktion als auch den Zustand.
Martin York

1
Nein, ein Closure ist weder eine "Compiler-Optimierung" noch ein syntaktischer Zucker. -1
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.