Wie würden Sie JavaScript-Verschlüsse jemandem erklären, der die Konzepte kennt, aus denen sie bestehen (z. B. Funktionen, Variablen und dergleichen), aber Verschlüsse selbst nicht versteht?
Ich habe das Schema-Beispiel auf Wikipedia gesehen, aber leider hat es nicht geholfen.
closure
Codes,Object Literal
der sich selbst wiederverwendet und den Overhead trotzdem reduziert, aber 100% weniger Wrap-Code erfordert.Antworten:
Ein Verschluss ist eine Paarung von:
Eine lexikalische Umgebung ist Teil jedes Ausführungskontexts (Stapelrahmen) und eine Zuordnung zwischen Bezeichnern (dh lokalen Variablennamen) und Werten.
Jede Funktion in JavaScript behält einen Verweis auf ihre äußere lexikalische Umgebung bei. Diese Referenz wird verwendet, um den Ausführungskontext zu konfigurieren, der beim Aufrufen einer Funktion erstellt wird. Diese Referenz ermöglicht es dem Code innerhalb der Funktion, außerhalb der Funktion deklarierte Variablen zu "sehen", unabhängig davon, wann und wo die Funktion aufgerufen wird.
Wenn eine Funktion von einer Funktion aufgerufen wurde, die wiederum von einer anderen Funktion aufgerufen wurde, wird eine Kette von Verweisen auf äußere lexikalische Umgebungen erstellt. Diese Kette wird als Scope-Kette bezeichnet.
Bildet im folgenden Code
inner
einen Abschluss mit der lexikalischen Umgebung des Ausführungskontexts, der beimfoo
Aufrufen erstellt wird, und schließt über die Variablesecret
:Mit anderen Worten: In JavaScript enthalten Funktionen einen Verweis auf eine private "Statusbox", auf die nur sie (und alle anderen Funktionen, die in derselben lexikalischen Umgebung deklariert sind) Zugriff haben. Diese Statusbox ist für den Aufrufer der Funktion unsichtbar und bietet einen hervorragenden Mechanismus zum Ausblenden und Einkapseln von Daten.
Und denken Sie daran: Funktionen in JavaScript können wie Variablen (erstklassige Funktionen) weitergegeben werden, was bedeutet, dass diese Paarungen von Funktionalität und Status in Ihrem Programm weitergegeben werden können: Ähnlich wie Sie eine Instanz einer Klasse in C ++ weitergeben könnten.
Wenn JavaScript keine Abschlüsse hätte, müsste explizit mehr Status zwischen Funktionen übergeben werden , wodurch die Parameterlisten länger und der Code lauter werden.
Wenn Sie also möchten, dass eine Funktion immer Zugriff auf einen privaten Status hat, können Sie einen Abschluss verwenden.
... und häufig möchten wir einen Zustand mit einer Funktion verknüpfen. Wenn Sie beispielsweise in Java oder C ++ einer Klasse eine private Instanzvariable und eine Methode hinzufügen, ordnen Sie den Status der Funktionalität zu.
In C und den meisten anderen gängigen Sprachen sind nach der Rückkehr einer Funktion nicht mehr alle lokalen Variablen verfügbar, da der Stapelrahmen 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. Auf diese Weise
secret
bleibt im obigen Code das Funktionsobjekt verfügbarinner
, nachdem es von zurückgegeben wurdefoo
.Verwendung von Verschlüssen
Schließungen sind immer dann nützlich, wenn Sie einen privaten Status benötigen, der einer Funktion zugeordnet ist. Dies ist ein sehr häufiges Szenario - und denken Sie daran: JavaScript hatte bis 2015 keine Klassensyntax und noch keine private Feldsyntax. Verschlüsse erfüllen diesen Bedarf.
Variablen für private Instanzen
Im folgenden Code wird die Funktion
toString
über die Details des Fahrzeugs geschlossen.Funktionale Programmierung
Im folgenden Code wird die Funktion
inner
überfn
und geschlossenargs
.Ereignisorientierte Programmierung
Im folgenden Code wird die Funktion
onClick
über der Variablen geschlossenBACKGROUND_COLOR
.Modularisierung
Im folgenden Beispiel sind alle Implementierungsdetails in einem sofort ausgeführten Funktionsausdruck verborgen. Die Funktionen
tick
undtoString
schließen über den privaten Staat und Funktionen, die sie benötigen, um ihre Arbeit abzuschließen. Durch Verschlüsse konnten wir unseren Code modularisieren und kapseln.Beispiele
Beispiel 1
Dieses Beispiel zeigt, dass die lokalen Variablen nicht in den Abschluss kopiert werden: Der Abschluss behält einen Verweis auf die ursprünglichen Variablen selbst bei . Es ist, als ob der Stapelrahmen auch nach dem Beenden der äußeren Funktion im Speicher lebendig bleibt.
Beispiel 2
Im folgenden Code, drei Methoden
log
,increment
undupdate
alle in der Nähe im gleichen lexikalischen Umgebung.Und jedes Mal
createObject
, wenn aufgerufen wird, wird ein neuer Ausführungskontext (Stapelrahmen) erstellt und eine völlig neue Variablex
sowie ein neuer Satz von Funktionen (log
usw.) erstellt, die über dieser neuen Variablen geschlossen werden.Beispiel 3
Wenn Sie Variablen verwenden
var
, die mit deklariert wurden , achten Sie darauf, dass Sie verstehen, über welche Variable Sie schließen. Mit deklarierte Variablenvar
werden gehisst. Dies ist aufgrund der Einführung vonlet
und in modernem JavaScript weitaus weniger problematischconst
.Im folgenden Code wird jedes Mal um die Schleife herum eine neue Funktion
inner
erstellt, die geschlossen wirdi
. Davar i
diese inneren Funktionen jedoch außerhalb der Schleife angehoben werden, schließen sie alle über dieselbe Variable, was bedeutet, dass der Endwert voni
(3) dreimal gedruckt wird.Endpunkte:
function
Funktion aus einer anderen Funktion ist das klassische Beispiel für einen Abschluss, da der Status innerhalb der äußeren Funktion der zurückgegebenen inneren Funktion implizit zur Verfügung steht, selbst nachdem die Ausführung der äußeren Funktion abgeschlossen wurde.eval()
innerhalb einer Funktion verwenden, wird ein Verschluss verwendet. Der Text, auf den Sieeval
auf lokale Variablen der Funktion verweisen können, und im nicht strengen Modus können Sie sogar neue lokale Variablen erstellen, indem Sie verwendeneval('var foo = …')
.new Function(…)
(den Funktionskonstruktor ) in einer Funktion verwenden, wird diese nicht über ihre lexikalische Umgebung geschlossen, sondern über den globalen Kontext. Die neue Funktion kann nicht auf die lokalen Variablen der äußeren Funktion verweisen.Links
quelle
Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created.
von developer.mozilla.org/en-US/docs/Web/JavaScript/Closureslet i = 0
anstelle vonvar i = 0
Beispiel 5 verwenden,testList()
wird das gedruckt, was Sie ursprünglich möchten.Jede Funktion in JavaScript unterhält eine Verknüpfung zu ihrer äußeren lexikalischen Umgebung. Eine lexikalische Umgebung ist eine Karte aller Namen (z. B. Variablen, Parameter) innerhalb eines Bereichs mit ihren Werten.
Wenn Sie also das
function
Schlüsselwort sehen, hat Code innerhalb dieser Funktion Zugriff auf Variablen, die außerhalb der Funktion deklariert wurden.Dies wird protokolliert,
16
da die Funktionbar
über dem Parameterx
und der Variablen geschlossen wirdtmp
, die beide in der lexikalischen Umgebung der äußeren Funktion vorhanden sindfoo
.Die Funktion
bar
ist zusammen mit ihrer Verbindung mit der lexikalischen Funktionsumgebungfoo
ein Abschluss.Eine Funktion muss nicht zurückkehren , um einen Abschluss zu erstellen. Einfach aufgrund seiner Deklaration schließt jede Funktion über ihrer einschließenden lexikalischen Umgebung und bildet einen Abschluss.
Die obige Funktion protokolliert auch 16, da der darin enthaltene Code
bar
weiterhin auf Argumentex
und Variablen verweisen kanntmp
, obwohl sie nicht mehr direkt im Gültigkeitsbereich sind.Da es jedoch
tmp
immer noch imbar
Verschluss herumhängt, kann es inkrementiert werden. Sie wird bei jedem Anruf erhöhtbar
.Das einfachste Beispiel für eine Schließung ist folgendes:
Wenn eine JavaScript-Funktion aufgerufen wird, wird ein neuer Ausführungskontext
ec
erstellt. Zusammen mit den Funktionsargumenten und dem Zielobjekt erhält dieser Ausführungskontext auch eine Verknüpfung zur lexikalischen Umgebung des aufrufenden Ausführungskontexts, dh, die in der äußeren lexikalischen Umgebung deklarierten Variablen (im obigen Beispiel sind sowohla
als auchb
) verfügbarec
.Jede Funktion erstellt einen Abschluss, da jede Funktion eine Verknüpfung zu ihrer äußeren lexikalischen Umgebung hat.
Beachten Sie, dass Variablen selbst innerhalb eines Abschlusses sichtbar sind und keine Kopien.
quelle
delete
fehlschlägt. Trotzdem wird die lexikalische Umgebung, die die Funktion als [[Scope]] mit sich herumträgt (und letztendlich beim Aufrufen als Basis für ihre eigene lexikalische Umgebung verwendet), bestimmt, wenn die Anweisung ausgeführt wird, die die Funktion definiert. Dies bedeutet , dass die Funktion wird über den gesamten Inhalt der Ausführung Umfang schließen, unabhängig davon , welche Werte sie bezieht sich eigentlich auf , und ob sie den Schutzbereich entweicht. Bitte schauen Sie sich die Abschnitte 13.2 und 10 in der Spezifikation anVORWORT: Diese Antwort wurde geschrieben, als die Frage lautete:
Ich bin mir ziemlich sicher, dass ich einer der wenigen war, die versucht haben, die ursprüngliche Frage wörtlich zu nehmen. Seitdem ist die Frage mehrmals mutiert, sodass meine Antwort jetzt unglaublich albern und fehl am Platz erscheint. Hoffentlich macht die allgemeine Idee der Geschichte einigen Spaß.
Ich bin ein großer Fan von Analogie und Metapher, wenn ich schwierige Konzepte erkläre. Lassen Sie mich also eine Geschichte ausprobieren.
Es war einmal:
Da war eine Prinzessin ...
Sie lebte in einer wundervollen Welt voller Abenteuer. Sie traf ihren Prinzen Charming, ritt mit einem Einhorn um ihre Welt, kämpfte gegen Drachen, begegnete sprechenden Tieren und vielen anderen fantastischen Dingen.
Aber sie würde immer wieder in ihre langweilige Welt der Hausarbeit und Erwachsenen zurückkehren müssen.
Und sie erzählte ihnen oft von ihrem neuesten erstaunlichen Abenteuer als Prinzessin.
Aber alles, was sie sehen würden, ist ein kleines Mädchen ...
... Geschichten über Magie und Fantasie erzählen.
Und obwohl die Erwachsenen von echten Prinzessinnen wussten, würden sie niemals an die Einhörner oder Drachen glauben, weil sie sie niemals sehen konnten. Die Erwachsenen sagten, dass sie nur in der Fantasie 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.
quelle
story()
Funktion festgehalten , die die einzige Schnittstelle darstellt, die dielittleGirl
Instanz in die Welt der Magie entlarvt.story
ist die Schließung, aber wäre der Codevar story = function() {}; return story;
dannlittleGirl
die Schließung gewesen. Zumindest habe ich den Eindruck, dass MDN 'private' Methoden mit Closures verwendet : "Diese drei öffentlichen Funktionen sind Closures, die dieselbe Umgebung verwenden."story
ist ein Abschluss, der auf die im Rahmen von bereitgestellte Umgebung verweistprincess
.princess
ist auch ein weiterer impliziter Abschluss, dh dasprincess
und daslittleGirl
würde jeden Verweis auf einparents
Array teilen , das in der Umgebung / dem BereichlittleGirl
existieren würde, in dem das existiert und dasprincess
definiert ist.princess
als geschrieben. Leider ist diese Geschichte in diesem Thread jetzt etwas fehl am Platz. Ursprünglich lautete die Frage: "Erklären Sie einem 5-Jährigen JavaScript-Schließungen". Meine Antwort war die einzige, die das überhaupt versuchte. Ich bezweifle nicht, dass es kläglich gescheitert wäre, aber zumindest hätte diese Antwort die Chance gehabt, das Interesse eines Fünfjährigen zu wecken.Wenn wir die Frage ernst nehmen, sollten wir herausfinden, was ein typischer 6-Jähriger kognitiv kann, obwohl zugegebenermaßen einer, der sich für JavaScript interessiert, nicht so typisch ist.
Zur Entwicklung der Kindheit: 5 bis 7 Jahre heißt es:
Wir können dieses Beispiel verwenden, um Verschlüsse wie folgt zu erklären:
Wir können dies in JavaScript wie folgt codieren:
Weitere Punkte, die erklären, warum Schließungen interessant sind:
makeKitchen()
, wenn aufgerufen wird, wird ein neuer Abschluss mit einem eigenen separaten erstellttrashBags
.trashBags
Variable befindet sich lokal im Inneren jeder Küche und ist nicht außerhalb zugänglich, sondern die innere Funktion auf dergetTrashBag
Grundstück hat Zugriff darauf.getTrashBag
Funktion zurückgeben, geschieht dies hier.quelle
Der Strohmann
Ich muss wissen, wie oft auf eine Schaltfläche geklickt wurde, und bei jedem dritten Klick etwas tun ...
Ziemlich offensichtliche Lösung
Dies wird nun funktionieren, greift jedoch in den äußeren Bereich ein, indem eine Variable hinzugefügt wird, deren einziger Zweck darin besteht, die Anzahl 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 den Ereignishandler aufzunehmen .
Betrachten Sie diese Option
Beachten Sie hier einige Dinge.
Im obigen Beispiel verwende ich das Schließverhalten von JavaScript. Durch dieses 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. Da die zurückgegebene Funktion Zugriff auf die interne Zählvariable hat (aufgrund des oben erläuterten Schließverhaltens), ergibt sich ein privater Bereich für die Verwendung durch das Ergebnis Funktion ... Nicht so einfach? Lassen Sie es uns verdünnen ...
Ein einfacher einzeiliger Verschluss
Alle Variablen außerhalb der zurückgegebenen Funktion stehen der zurückgegebenen Funktion zur Verfügung, stehen dem zurückgegebenen Funktionsobjekt jedoch nicht direkt zur Verfügung ...
Kapiert? In unserem primären Beispiel ist die Zählvariable im Abschluss enthalten und steht dem Ereignishandler immer zur Verfügung, sodass der Status von Klick zu Klick beibehalten wird.
Auf diesen Status der privaten Variablen kann sowohl für das Lesen als auch für die Zuweisung zu den Variablen mit privatem Bereich vollständig zugegriffen werden.
Los geht's; Sie kapseln dieses Verhalten jetzt vollständig ein.
Vollständiger Blog-Beitrag (einschließlich Überlegungen zu jQuery)
quelle
Abschlüsse sind schwer zu erklären, da sie dazu dienen, ein Verhalten zum Funktionieren zu bringen, von dem jeder intuitiv erwartet, dass es funktioniert. Ich finde den besten Weg, sie zu erklären (und wie ich gelernt habe, was sie tun), mir die Situation ohne sie vorzustellen:
Was würde hier passieren, wenn JavaScript keine Schließungen kennen würde? Ersetzen Sie einfach den Aufruf in der letzten Zeile durch seinen Methodenkörper (was im Grunde das ist, was Funktionsaufrufe tun) und Sie erhalten:
Wo ist die Definition von
x
? Wir haben es im aktuellen Bereich nicht definiert. Die einzige Lösung besteht darin, den Geltungsbereich (oder besser gesagt den Geltungsbereich der Eltern)plus5
herumtragen zu lassen . Auf diese Weisex
ist es genau definiert und an den Wert 5 gebunden.quelle
OK, 6 Jahre alter Verschlussventilator. Möchten Sie das einfachste Beispiel für eine 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 die ausführlichere Erklärung oder das folgende Beispiel an.
So kann ich meine Flugzeuggeschichte in den Code konvertieren.
quelle
TLDR
Ein Abschluss ist eine Verbindung zwischen einer Funktion und ihrer äußeren lexikalischen Umgebung (dh wie geschrieben), sodass die in dieser Umgebung definierten Bezeichner (Variablen, Parameter, Funktionsdeklarationen usw.) innerhalb der Funktion sichtbar sind, unabhängig davon, wann oder von wo die Funktion aufgerufen wird.
Einzelheiten
In der Terminologie der ECMAScript-Spezifikation kann gesagt werden, dass ein Abschluss durch die
[[Environment]]
Referenz jedes Funktionsobjekts implementiert wird , was auf die lexikalische Umgebung verweist, in der die Funktion definiert ist.Wenn eine Funktion über die interne
[[Call]]
Methode aufgerufen wird, wird die[[Environment]]
Referenz auf das Funktionsobjekt in die äußere Umgebungsreferenz des Umgebungsdatensatzes des neu erstellten Ausführungskontexts (Stapelrahmen) kopiert .Im folgenden Beispiel wird die Funktion
f
über der lexikalischen Umgebung des globalen Ausführungskontexts geschlossen:Im folgenden Beispiel wird die Funktion
h
über der lexikalischen Funktionsumgebung geschlosseng
, die wiederum über der lexikalischen Umgebung des globalen Ausführungskontexts geschlossen wird.Wenn eine innere Funktion von einer äußeren zurückgegeben wird, bleibt die äußere lexikalische Umgebung bestehen, nachdem die äußere Funktion zurückgekehrt ist. Dies liegt daran, dass die äußere lexikalische Umgebung verfügbar sein muss, wenn die innere Funktion schließlich aufgerufen wird.
Im folgenden Beispiel wird die Funktion
j
über der lexikalischen Umgebung der Funktion geschlossen. Diesi
bedeutet, dass die Variablex
von der inneren Funktion aus sichtbar istj
, lange nachdem die Funktioni
die Ausführung abgeschlossen hat:In einem Abschluss sind die Variablen in der äußeren lexikalischen Umgebung selbst verfügbar, keine Kopien.
Die Kette lexikalischer Umgebungen, die über Verweise auf die äußere Umgebung zwischen Ausführungskontexten verbunden ist, bildet a Bereichskette und definiert die Bezeichner, die für eine bestimmte Funktion sichtbar sind.
Bitte beachten Sie, dass diese Antwort in dem Versuch, die Klarheit und Genauigkeit zu verbessern, gegenüber dem Original erheblich geändert wurde.
quelle
console.log
. Wenn jemand anderes interessiert ist, gibt es mehr: developer.mozilla.org/en-US/docs/DOM/…var
).Dies ist ein Versuch, einige (mögliche) Missverständnisse über Schließungen auszuräumen, die in einigen der anderen Antworten vorkommen.
quelle
Ich habe vor einiger Zeit einen Blog-Beitrag geschrieben, in dem Schließungen erklärt wurden. Hier ist, was ich über Schließungen gesagt habe, um herauszufinden, warum Sie eine wollen.
In diesem Sinne lassen sie eine Funktion ein bisschen wie ein Objekt mit privaten Attributen wirken.
Vollständiger Beitrag:
Also, was sind diese Verschlusssachen?
quelle
devError = emailError("[email protected]", errorString)
und dann meine eigene benutzerdefinierte Version einer freigegebenen emailError-Funktion haben.Verschlüsse sind einfach:
Das folgende einfache Beispiel behandelt alle Hauptpunkte von JavaScript-Schließungen. * *
Hier ist eine Fabrik, die Taschenrechner herstellt, die addieren und multiplizieren können:
Der entscheidende Punkt: Jeder Aufruf von
make_calculator
erstellt eine neue lokale Variablen
, die von diesem Rechner weiterhin verwendet werden kannadd
undmultiply
lange nach dermake_calculator
Rückkehr funktioniert .Wenn Sie mit Stapelrahmen vertraut sind, scheint dieser Rechner seltsam: Wie können sie halten den Zugriff
n
nachmake_calculator
Rückgabe ? Die Antwort ist, sich vorzustellen, dass JavaScript keine "Stapelrahmen" verwendet, sondern "Heap-Rahmen", die nach dem Funktionsaufruf, der sie zurückgegeben hat, bestehen bleiben können.Innere Funktionen wie
add
undmultiply
, die auf Variablen zugreifen, die in einer äußeren Funktion ** deklariert sind, werden als Closures bezeichnet .Das ist so ziemlich alles, was es zu Schließungen gibt.
* Zum Beispiel werden alle Punkte im Artikel "Closures for Dummies" behandelt, die in einer anderen Antwort angegeben sind , mit Ausnahme von Beispiel 6, in dem lediglich gezeigt wird, dass Variablen verwendet werden können, bevor sie deklariert werden. Dies ist eine nette Tatsache, aber völlig unabhängig von Closures. Es werden auch alle Punkte in der akzeptierten Antwort behandelt , mit Ausnahme der Punkte (1), bei denen Funktionen ihre Argumente in lokale Variablen kopieren (die benannten Funktionsargumente), und (2) beim Kopieren von Zahlen wird eine neue Zahl erstellt, beim Kopieren jedoch eine Objektreferenz gibt Ihnen einen weiteren Verweis auf dasselbe Objekt. Diese sind ebenfalls gut zu wissen, aber auch hier völlig unabhängig von Schließungen. Es ist auch dem Beispiel in sehr ähnlich dieser Antwort aber etwas kürzer und weniger abstrakt. Es geht nicht um den Punkt vonDiese Antwort oder dieser Kommentar besagt Strom, was bedeutet, dass JavaScript es schwierig macht, das anzuschließenWert 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 angeschlossen ist.) Auch dies ist sehr nützlich, wenn Sie Verschlüsse erstellen, aber nicht Teil dessen, was ein Verschluss ist oder wie er funktioniert. Es gibt zusätzliche Verwirrung aufgrund von Verschlüssen, die in funktionalen Sprachen wie ML unterschiedlich funktionieren, wobei Variablen eher an Werte als an Speicherplatz gebunden sind und einen konstanten Strom von Personen bereitstellen, die Verschlüsse auf eine Art und Weise verstehen (nämlich das "Einstecken") Einfach falsch für JavaScript, wo 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 klar hervorhebt.
quelle
first_calculator
es sich um ein Objekt (keine Funktion) handelt, sollten Sie keine Klammern verwendensecond_calculator = first_calculator;
, da es sich um eine Zuweisung und nicht um einen Funktionsaufruf handelt. Um Ihre Frage zu beantworten, würde es nur einen Aufruf von make_calculator geben, sodass nur ein Rechner ausgeführt würde, und die Variablen first_calculator und second_calculator würden sich beide auf denselben Rechner beziehen, sodass die Antworten 3, 403, 4433, 44330 wären.Wie ich es einem Sechsjährigen erklären würde:
Sie wissen, wie Erwachsene ein Haus besitzen können, und sie nennen es Zuhause? Wenn eine Mutter ein Kind hat, besitzt das Kind eigentlich nichts, oder? Aber seine Eltern besitzen ein Haus. Wenn also jemand das Kind fragt "Wo ist dein Zuhause?", Kann es "dieses Haus!" Antworten 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 Zuhause hat, obwohl es wirklich die Eltern sind, denen das Haus gehört.
quelle
Können Sie einem 5-Jährigen die Schließung erklären? *
Ich denke immer noch, dass Googles Erklärung sehr gut funktioniert und prägnant ist:
* AC # Frage
quelle
Ich neige dazu, durch gute / schlechte Vergleiche besser zu lernen. Ich mag es, Arbeitscode gefolgt von nicht funktionierendem Code zu sehen, auf den wahrscheinlich jemand stößt. Ich habe eine jsFiddle zusammengestellt , die einen Vergleich durchführt und versucht, die Unterschiede auf die einfachsten Erklärungen zu reduzieren , die mir einfallen könnten.
Schließungen richtig gemacht:
Im obigen Code
createClosure(n)
wird in jeder Iteration der Schleife aufgerufen. Beachten Sie, dass ich die Variable benannt habe,n
um hervorzuheben, dass es sich um eine neue handelt Variable handelt, die in einem neuen Funktionsbereich erstellt wurde und nicht dieselbe Variableindex
ist, die an den äußeren Bereich gebunden ist.Dies schafft einen neuen Bereich 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 Bereichs
n
ist an den Wert gebunden, den es beimcreateClosure(n)
Aufrufen hatte, sodass die verschachtelte Funktion, die zurückgegeben wird, immer den Wert zurückgibtn
, den sie beim Zeitpunkt hattecreateClosure(n)
Aufrufen hatte.Verschlüsse falsch gemacht:
Im obigen Code wurde die Schleife innerhalb der verschoben
createClosureArray()
Funktion und die Funktion gibt jetzt nur das fertige Array zurück, was auf den ersten Blick intuitiver erscheint.Was vielleicht nicht offensichtlich ist, ist das seitdem
createClosureArray()
nur aufgerufen wird, nur ein Bereich für diese Funktion erstellt wird, anstatt 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 zurückkehrenden Array Funktionen hinzuindex
. Beachten Sie, dassindex
innerhalb der definiert istcreateClosureArray
Funktion die immer nur einmal aufgerufen wird.Da es innerhalb der
createClosureArray()
Funktion nur einen Bereich gab ,index
ist dieser nur an einen Wert innerhalb dieses Bereichs gebunden. Mit anderen Worten, jedes Mal, wenn die Schleife den Wert von ändertindex
ändert, ändert sie ihn für alles, was innerhalb dieses Bereichs auf ihn verweist.Alle dem Array hinzugefügten Funktionen geben die
index
Variable SAME aus dem übergeordneten Bereich zurück, in dem sie definiert wurde, anstatt 10 verschiedene aus 10 verschiedenen Bereichen wie im ersten Beispiel. 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 dem Array hinzugefügte Funktion den Wert der einzelnenindex
Variablen zurück, die jetzt auf 10 gesetzt ist.Ergebnis
quelle
let
for wirdvar
der Unterschied behoben.n
die in einem neuen Abschluss erstellt wurden. Wir geben nur eine Funktion zurück, damit wir sie im Array speichern und später aufrufen können.arr[index] = (function (n) { return 'n = ' + n; })(index);
. Aber dann speichern Sie die resultierende Zeichenfolge im Array und nicht eine aufzurufende Funktion, die den Punkt meines Beispiels zunichte macht.Wikipedia zu Schließungen :
Technisch gesehen , in JavaScript , ist jede Funktion ein Verschluss . 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, meinen wir unter Schließen in JavaScript normalerweise eine Funktion, die mit nichtlokalen Variablen arbeitet, die in bereits ausgeführten Umgebungsfunktionen definiert sind .
Verschlüsse werden häufig zum Erstellen von Funktionen mit versteckten privaten Daten verwendet (dies ist jedoch nicht immer der Fall).
ems
Im obigen Beispiel wird eine anonyme Funktion verwendet, die einmal ausgeführt wurde. Das 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 Anwendungsbeispiel für Schließungen 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.quelle
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:
quelle
Die Geheimnisse für JavaScript-Funktionen sind die privaten Variablen
Jedes Mal, wenn Sie es aufrufen, wird die lokale Variable "name" erstellt und mit "Mary" versehen. Und jedes Mal, wenn die Funktion beendet wird, geht die Variable verloren und der Name wird vergessen.
Wie Sie vielleicht erraten haben, muss es einen geheimen Ort geben, an dem sie gespeichert werden, da die Variablen bei jedem Aufruf der Funktion neu erstellt werden und niemand anderes sie kennt. Es könnte als Kammer der Geheimnisse oder Stapel oder lokaler Bereich bezeichnet werden aber es spielt keine Rolle. Wir wissen, dass sie irgendwo in der Erinnerung 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.
Solange wir uns in der übergeordneten Funktion befinden, können eine oder mehrere untergeordnete Funktionen erstellt werden, die die geheimen Variablen vom geheimen Ort 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
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 "Alice" nennen, wird es antworten
Das ist alles was es zu erzählen gibt.
quelle
Ich verstehe nicht, warum die Antworten hier so komplex sind.
Hier ist eine Schließung:
Ja. Sie verwenden das wahrscheinlich mehrmals am Tag.
quelle
a
. Meiner Meinung nach ist die Überraschung, dass das JS-Bereichsobjekt effektiva
als Eigenschaft und nicht als Konstante bereitgestellt wird. Und Sie werden dieses wichtige Verhalten nur bemerken, wenn Sie es ändern, wie inreturn a++;
Beispiel für den ersten Punkt von dlaliberte:
quelle
Bei einem Abschluss hat eine innere Funktion Zugriff auf Variablen in ihrer äußeren Funktion. Dies ist wahrscheinlich die einfachste einzeilige Erklärung, die Sie für Schließungen erhalten können.
quelle
Ich weiß, dass es bereits viele Lösungen gibt, aber ich denke, dass dieses kleine und einfache Skript nützlich sein kann, um das Konzept zu demonstrieren:
quelle
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?
quelle
Der Autor von Closures hat Closures ziemlich gut erklärt, den Grund, warum wir sie brauchen, und LexicalEnvironment, das zum Verständnis von Closures erforderlich ist.
Hier ist die Zusammenfassung:
Was ist, wenn auf eine Variable zugegriffen wird, diese jedoch nicht lokal ist? Wie hier:
In diesem Fall findet der Interpreter die Variable im äußeren
LexicalEnvironment
Objekt.Der Prozess besteht aus zwei Schritten:
Wenn eine Funktion erstellt wird, erhält sie eine versteckte Eigenschaft mit dem Namen [[Scope]], die auf die aktuelle LexicalEnvironment verweist.
Wenn eine Variable gelesen wird, aber nirgendwo gefunden werden kann, 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.
Die Funktion g hat also Zugriff auf g, a und f.
Verschlüsse
Eine verschachtelte Funktion kann nach Abschluss der äußeren Funktion weiter ausgeführt werden:
Markieren von LexicalEnvironments:
Wie wir sehen,
this.say
handelt es sich um eine Eigenschaft im Benutzerobjekt, die nach Abschluss des Benutzers weiterhin aktiv ist.Und wenn Sie sich erinnern, wann
this.say
es erstellt wird, erhält es (wie jede Funktion) eine interne Referenzthis.say.[[Scope]]
beim Erstellen auf die aktuelle LexicalEnvironment. Die LexicalEnvironment der aktuellen Benutzerausführung bleibt also im Speicher. Alle Variablen des Benutzers sind auch seine Eigenschaften, daher werden sie auch sorgfältig aufbewahrt und nicht wie üblich verschmutzt.Der springende Punkt ist sicherzustellen, dass die innere Funktion, wenn sie in Zukunft auf eine äußere Variable zugreifen möchte, dies tun kann.
Zusammenfassen:
Dies wird als Schließung bezeichnet.
quelle
JavaScript-Funktionen können auf Folgendes zugreifen:
Wenn eine Funktion auf ihre Umgebung zugreift, ist die Funktion ein Abschluss.
Beachten Sie, dass äußere Funktionen nicht erforderlich sind, obwohl sie Vorteile bieten, die ich hier nicht diskutiere. Durch den Zugriff auf Daten in seiner Umgebung hält ein Abschluss diese Daten am Leben. Im Unterfall der ä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 Verlassen der äußeren Funktion überleben, behalten die inneren Funktionen die lokalen Daten der äußeren Funktion am Leben.
Beispiel für einen Abschluss, der die globale Umgebung verwendet:
Stellen Sie sich vor, dass die Ereignisse für die Stapelüberlauf-Abstimmungs- und Abstimmungsschaltflächen als Abschlüsse, 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-Abstimmungs-Schaltflächen von StackOverflow, nicht auf die Reihe der Antwort-Abstimmungs-Schaltflä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 ein Abschluss, da sie auf ihre Umgebung zugreift.
Alle vier Funktionen sind Schließungen, da sie alle auf ihre Umgebung zugreifen.
quelle
Als Vater eines 6-Jährigen, der derzeit kleine Kinder unterrichtet (und ein relativer Anfänger im Codieren ohne formale Ausbildung, sodass Korrekturen erforderlich sind), denke ich, dass die Lektion am besten durch praktisches Spielen erhalten bleibt. Wenn der 6-Jährige bereit ist zu verstehen, was eine Schließung ist, ist er 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 folgende erläuternde Text ist wahrscheinlich besser für einen 10-Jährigen geeignet.
ANLEITUNG
DATEN: Daten sind eine Sammlung von Fakten. Es können Zahlen, Wörter, Maße, Beobachtungen oder auch nur Beschreibungen von Dingen sein. Sie können es nicht berühren, riechen oder schmecken. Sie können es aufschreiben, sprechen und hören. Sie können es zum Erstellen verwenden mit einem Computer einen Geruch Geruch und Geschmack . Es kann von einem Computer unter Verwendung von Code nützlich gemacht werden.
CODE: Alle oben genannten Schriften 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 jedem sagt, dass dies "jeder setzt sich" bedeutet - also tun Sie es 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 werden als Funktionen bezeichnet.
FUNKTION: Eine Funktion in JavaScript ist wie eine Fabrik. Es könnte eine kleine Fabrik mit nur einer Maschine sein. Oder es enthält viele andere kleine Fabriken mit jeweils vielen Maschinen, die unterschiedliche Aufgaben ausführen. In einer echten Kleiderfabrik könnten Unmengen von Stoffen und Fadenspulen hineingehen und T-Shirts und Jeans herauskommen. Unsere JavaScript-Fabrik verarbeitet nur Daten, kann weder nähen, bohren noch Metall schmelzen. In unserer JavaScript-Factory werden Daten eingegeben und Daten 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 zum 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 darüber liefern, 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:
Beachten Sie dies
/*...*/
und//
stoppen Sie das Lesen von Code durch den Browser.NAME: Sie können eine Funktion so gut wie jedes gewünschte Wort aufrufen. Das Beispiel "cookMeal" ist typisch dafür, zwei Wörter zusammenzufügen und dem zweiten zu Beginn einen Großbuchstaben zu geben - dies ist jedoch nicht erforderlich. Es kann kein Leerzeichen enthalten und es kann keine eigene Zahl sein.
ELTERN: "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 die Postbox markiert werden zum BeispielcookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime)
, in dem Fall , dass Sie wissen , welche Daten Sie es geben.STREBEN: "Zahnspangen", die so aussehen,
{}
sind die getönten Scheiben unserer Fabrik. Von innerhalb der Fabrik kann man sehen, aber von außen kann man nicht sehen.DAS LANGE CODE-BEISPIEL OBEN
Unser Code beginnt mit dem Wort - Funktion , so dass wir wissen , dass es eine ist! Dann singt der Name der Funktion - das ist meine eigene Beschreibung dessen, worum es in der Funktion geht. Dann 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 gibt es eine solche Klammer{
. Dies markiert den Start der Funktion sing () . Es hat einen Partner, der das Ende von sing () so markiert}
Diese Funktion hat möglicherweise etwas mit dem Singen zu tun und benötigt möglicherweise Daten über eine Person. Es enthält Anweisungen, um etwas mit diesen Daten zu tun.
Nach der Funktion sing () befindet sich am Ende des Codes die Zeile
VARIABLE: Die Buchstaben var stehen für "variable". 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, die wie ein Stück Schnur (als Schnur bezeichnet) miteinander verbunden sind und eine Phrase mit der Aufschrift "eine alte Dame" bilden. Unser Umschlag kann andere Arten von Dingen enthalten, wie Zahlen (als Ganzzahlen bezeichnet), Anweisungen (als Funktionen bezeichnet), Listen (als Arrays bezeichnet ). 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 überall im Code aus gesehen werden. Wir nennen dies eine "globale Variable".GLOBAL VARIABLE: Person ist eine globale Variable. Wenn Sie also ihren Wert von "einer alten Dame" in "einen jungen Mann" ändern , bleibt die Person ein junger Mann, bis Sie sich entscheiden, ihn erneut zu ändern, und dass jede andere Funktion in Der Code kann sehen, dass es ein junger Mann ist. Drücken Sie die F12Taste oder sehen Sie sich die Optionen an, um die Entwicklerkonsole eines Browsers zu öffnen, und geben Sie "person" ein, um zu sehen, was dieser Wert ist. Geben Sie ein
person="a young man"
, um es zu ändern, und geben Sie dann erneut "Person" ein, um festzustellen, ob es sich geändert hat.Danach haben wir die Leitung
Diese Zeile ruft die Funktion auf, als würde sie einen Hund aufrufen
Wenn der Browser den JavaScript-Code geladen hat und diese Zeile erreicht hat, wird die Funktion gestartet. Ich setze die Zeile am Ende, um sicherzustellen, dass der Browser alle Informationen hat, die er zum Ausführen benötigt.
Funktionen definieren Aktionen - die Hauptfunktion ist das Singen. Es enthält eine Variable namens firstPart, die für das Singen über die Person gilt, die für jeden der Verse des Liedes gilt: "Es gab" + Person + ", die geschluckt hat". Wenn Sie firstPart in die Konsole eingeben , erhalten Sie keine Antwort, da die Variable in einer Funktion eingeschlossen ist - der Browser kann in den getönten Fenstern der geschweiften Klammern nicht sehen.
SCHLIESSUNGEN: Die Verschlüsse sind die kleineren Funktionen, die sich innerhalb der großen sing () -Funktion befinden. Die kleinen Fabriken in der großen Fabrik. Sie haben jeweils ihre eigenen Klammern, was bedeutet, dass die Variablen in ihnen von außen nicht sichtbar sind. Aus diesem Grund können die Namen der Variablen ( Kreatur und Ergebnis ) in den Abschlüssen wiederholt werden, jedoch mit unterschiedlichen Werten. Wenn Sie diese Variablennamen in das Konsolenfenster eingeben, wird der Wert nicht angezeigt, da er von zwei Ebenen getönter Fenster ausgeblendet wird.
Die Verschlüsse wissen alle, wie die Variable firstPart der Funktion sing () lautet, da sie aus ihren getönten Fenstern heraussehen können.
Nach den Schließungen kommen die Linien
Die Funktion sing () ruft jede dieser Funktionen in der angegebenen Reihenfolge auf. Dann wird die Arbeit der Funktion sing () erledigt.
quelle
Okay, wenn ich mit einem 6-jährigen Kind spreche, würde ich möglicherweise folgende Assoziationen verwenden.
Vergleichen Sie mit einer Situation, in der eine Tür durch Zugluft verschlossen war und niemand drinnen war (allgemeine Funktionsausführung) und dann ein lokales Feuer auftrat und den Raum niederbrannte (Müllsammler: D), und dann ein neuer Raum gebaut wurde und Sie jetzt gehen können ein anderes Spielzeug dort (neue Funktionsinstanz), aber nie die gleichen Spielzeuge bekommen, die in der ersten Rauminstanz übrig waren.
Für ein fortgeschrittenes Kind würde ich so etwas wie das Folgende sagen. Es ist nicht perfekt, aber man fühlt sich wie es ist:
Wie Sie sehen können, sind die im Raum zurückgelassenen Spielzeuge weiterhin über den Bruder zugänglich, unabhängig davon, ob der Raum verschlossen ist. Hier ist ein Jsbin , mit dem man herumspielen kann.
quelle
Eine Antwort für einen Sechsjährigen (vorausgesetzt er weiß, was eine Funktion ist und was eine Variable ist und welche Daten es 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 nicht 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 hineinschauen und die verwendeten Variablen sehen, außer der zurückgegebenen Funktion. Diese neue Funktion hat eine besondere Fähigkeit, in die Funktion, die sie erstellt hat, zurückzublicken und die darin enthaltenen Daten anzuzeigen.
Eine andere wirklich einfache Möglichkeit, dies zu erklären, ist der 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.
quelle
Eine Funktion in JavaScript ist nicht nur eine Referenz auf eine Reihe von Anweisungen (wie in der Sprache C), sondern enthält auch eine versteckte Datenstruktur, die aus Verweisen auf alle von ihr verwendeten nichtlokalen Variablen (erfasste Variablen) besteht. Solche zweiteiligen Funktionen werden Verschlüsse genannt. Jede Funktion in JavaScript kann als Abschluss betrachtet werden.
Verschlüsse sind Funktionen mit einem Zustand. Es ist "dies" in dem Sinne etwas ähnlich, dass "dies" auch den Status für eine Funktion bereitstellt, aber Funktion und "dies" separate Objekte sind ("dies" ist nur ein ausgefallener Parameter und die einzige Möglichkeit, ihn dauerhaft an a zu binden Funktion ist es, einen Verschluss zu erstellen). Während "dies" und die Funktion immer getrennt 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 einer Root-Funktion angenommen werden) und jede einzelne Ausführung einer Funktion neue Instanzen von erstellt Aus seinen lokalen Variablen folgt, dass jede Ausführung einer Funktion, die eine verschachtelte Funktion zurückgibt (oder auf andere Weise überträgt, z. B. als Rückruf registriert), einen neuen Abschluss erzeugt (mit einem eigenen potenziell eindeutigen Satz referenzierter nichtlokaler Variablen, die ihre Ausführung darstellen Kontext).
Es muss auch verstanden werden, dass lokale Variablen in JavaScript nicht auf dem Stapelrahmen, 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 weiterhin ungleich Null sein, wenn sie während der aktuellen Ausführung Teil eines Abschlusses wurden und weiterhin von ihren lexikalisch verschachtelten Funktionen referenziert werden (was nur möglich ist, wenn die Verweise auf Diese verschachtelten Funktionen wurden zurückgegeben oder auf andere Weise an einen externen Code übertragen.
Ein Beispiel:
quelle
Vielleicht ein bisschen mehr als alles andere als der frühreifste Sechsjährige, aber ein paar Beispiele, die dazu beigetragen haben, dass das Konzept der Schließung in JavaScript für mich klickt.
Ein Abschluss ist eine Funktion, die Zugriff auf den Bereich einer anderen Funktion (ihre Variablen und Funktionen) hat. Der einfachste Weg, einen Abschluss zu erstellen, ist eine Funktion innerhalb einer Funktion. Der Grund dafür ist, dass eine Funktion in JavaScript immer Zugriff auf den Gültigkeitsbereich ihrer enthaltenen Funktion hat.
ALERT: 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:
ALERT: Affe
referenceToInnerFunction wird auf OuterFunction () gesetzt, wodurch einfach ein Verweis auf innerFunction zurückgegeben wird. Wenn referenceToInnerFunction aufgerufen wird, wird OuterVar zurückgegeben. Wie oben zeigt dies erneut, 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, sagen wir, setzen Sie es auf Null. Sie könnten denken, dass referenceToInnerFunction seinen Zugriff auf den Wert von OuterVar verlieren würde. Dies ist jedoch nicht der Fall.
ALERT: Affe ALERT: Affe
Aber wie ist das so? Wie kann referenceToInnerFunction den Wert von OuterVar noch kennen, nachdem OuterFunction auf Null gesetzt wurde?
Der Grund, warum referenceToInnerFunction weiterhin auf den Wert von OuterVar zugreifen kann, liegt darin, dass InnerFunction beim Erstellen des Abschlusses durch Platzieren von InnerFunction innerhalb von OuterFunction einen Verweis auf den Bereich von OuterFunction (seine Variablen und Funktionen) zu seiner Bereichskette hinzugefügt hat. Dies bedeutet, dass innerFunction einen Zeiger oder Verweis auf alle Variablen von OuterFunction hat, einschließlich OuterVar. Selbst wenn die Ausführung von OuterFunction abgeschlossen ist oder wenn sie gelöscht oder auf Null gesetzt wird, bleiben die Variablen in ihrem Gültigkeitsbereich wie OuterVar im Speicher, da die InnerFunction, auf die zurückgegeben wurde, noch nicht auf sie verweist referenceToInnerFunction. Um OuterVar und den Rest der Variablen von OuterFunction wirklich aus dem Speicher freizugeben, müssten Sie diesen hervorragenden Verweis auf sie entfernen.
///////////
Zwei weitere Dinge zu Schließungen zu beachten. Erstens hat der Abschluss immer Zugriff auf die letzten Werte seiner enthaltenen Funktion.
Alarm: Gorilla
Zweitens behält ein Abschluss beim Erstellen einen Verweis auf alle Variablen und Funktionen seiner umschließenden Funktion bei. es kann nicht auswählen. Und so sollten Verschlüsse sparsam oder zumindest vorsichtig verwendet werden, da sie speicherintensiv sein können. Viele Variablen können lange nach Beendigung der Ausführung einer enthaltenen Funktion im Speicher gespeichert werden.
quelle
Ich würde sie einfach auf die Seite Mozilla Closures verweisen . Es ist die beste, prägnanteste und einfachste 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 Schließungen lernt, ist es logisch, dass er bereit ist, die prägnante und einfache Erklärung im Artikel zu verstehen .
quelle