Ich stellte eine Frage zu Currying und Verschlüsse wurden erwähnt. Was ist eine Schließung? In welcher Beziehung steht es zum Curry?
432
Ich stellte eine Frage zu Currying und Verschlüsse wurden erwähnt. Was ist eine Schließung? In welcher Beziehung steht es zum Curry?
Antworten:
Variabler Umfang
Wenn Sie eine lokale Variable deklarieren, hat diese Variable einen Gültigkeitsbereich. Im Allgemeinen existieren lokale Variablen nur innerhalb des Blocks oder der Funktion, in der Sie sie deklarieren.
Wenn ich versuche, auf eine lokale Variable zuzugreifen, suchen die meisten Sprachen im aktuellen Bereich danach und dann durch die übergeordneten Bereiche, bis sie den Stammbereich erreichen.
Wenn ein Block oder eine Funktion erledigt ist, werden seine lokalen Variablen nicht mehr benötigt und sind normalerweise nicht mehr genügend Speicher.
So erwarten wir normalerweise, dass die Dinge funktionieren.
Ein Abschluss ist ein persistenter lokaler Variablenbereich
Ein Abschluss ist ein dauerhafter Bereich, der lokale Variablen auch dann beibehält, wenn die Codeausführung diesen Block verlassen hat. In Sprachen, die das Schließen unterstützen (wie JavaScript, Swift und Ruby), können Sie einen Verweis auf einen Bereich (einschließlich der übergeordneten Bereiche) beibehalten, auch nachdem der Block, in dem diese Variablen deklariert wurden, die Ausführung abgeschlossen hat, sofern Sie einen Verweis behalten zu diesem Block oder Funktion irgendwo.
Das Bereichsobjekt und alle seine lokalen Variablen sind an die Funktion gebunden und bleiben bestehen, solange diese Funktion bestehen bleibt.
Dies gibt uns Funktionsportabilität. Wir können davon ausgehen, dass alle Variablen, die zum Zeitpunkt der ersten Definition der Funktion im Gültigkeitsbereich waren, beim späteren Aufruf der Funktion noch im Gültigkeitsbereich sind, auch wenn wir die Funktion in einem völlig anderen Kontext aufrufen.
Zum Beispiel
Hier ist ein wirklich einfaches Beispiel in JavaScript, das den Punkt veranschaulicht:
Hier habe ich eine Funktion innerhalb einer Funktion definiert. Die innere Funktion erhält Zugriff auf alle lokalen Variablen der äußeren Funktion, einschließlich
a
. Die Variablea
ist im Bereich für die innere Funktion.Normalerweise werden beim Beenden einer Funktion alle lokalen Variablen weggeblasen. Wenn wir jedoch die innere Funktion zurückgeben und sie einer Variablen zuweisen,
fnc
sodass sie nach demouter
Beenden bestehen bleibt , bleiben auch alle Variablen erhalten, die zuminner
Zeitpunkt der Definition im Gültigkeitsbereich waren . Die Variablea
wurde geschlossen - sie befindet sich innerhalb eines Abschlusses.Beachten Sie, dass die Variable
a
für völlig privat istfnc
. Auf diese Weise können private Variablen in einer funktionalen Programmiersprache wie JavaScript erstellt werden.Wie Sie vielleicht erraten können, gibt es beim Aufrufen
fnc()
den Wert vona
"1" aus.In einer Sprache ohne Abschluss
a
wäre die Variable beimouter
Beenden der Funktion durch Müll gesammelt und weggeworfen worden . Das Aufrufen von fnc hätte einen Fehler ausgelöst, da diesera
nicht mehr vorhanden ist.In JavaScript
a
bleibt die Variable bestehen, da der Variablenbereich beim ersten Deklarieren der Funktion erstellt wird und so lange bestehen bleibt, wie die Funktion weiterhin besteht.a
gehört zum Geltungsbereich vonouter
. Der Bereich voninner
hat einen übergeordneten Zeiger auf den Bereich vonouter
.fnc
ist eine Variable, die auf zeigtinner
.a
bleibt so langefnc
bestehen.a
ist innerhalb der Schließung.quelle
Ich werde ein Beispiel geben (in JavaScript):
Diese Funktion, makeCounter, gibt eine Funktion zurück, die wir x genannt haben und die bei jedem Aufruf um eins hochzählt. Da wir x keine Parameter zur Verfügung stellen, muss es sich irgendwie an die Anzahl erinnern. Es weiß, wo es zu finden ist, basierend auf dem sogenannten lexikalischen Scoping - es muss auf die Stelle schauen, an der es definiert ist, um den Wert zu finden. Dieser "versteckte" Wert wird als Abschluss bezeichnet.
Hier ist noch einmal mein Curry-Beispiel:
Wenn Sie add mit dem Parameter a (3) aufrufen, ist dieser Wert im Abschluss der zurückgegebenen Funktion enthalten, die wir als add3 definieren. Auf diese Weise weiß es, wenn wir add3 aufrufen, wo sich der a-Wert befindet, um die Addition durchzuführen.
quelle
Kyles Antwort ist ziemlich gut. Ich denke, die einzige zusätzliche Klarstellung ist, dass der Abschluss im Grunde eine Momentaufnahme des Stapels an dem Punkt ist, an dem die Lambda-Funktion erstellt wird. Wenn die Funktion erneut ausgeführt wird, wird der Stapel in diesem Zustand wiederhergestellt, bevor die Funktion ausgeführt wird. Wie Kyle erwähnt, ist dieser versteckte Wert (
count
) verfügbar, wenn die Lambda-Funktion ausgeführt wird.quelle
Erstens ist die Schließung im Gegensatz zu dem, was die meisten Leute hier sagen, keine Funktion ! Also was ist das?
Es handelt sich um eine Reihe von Symbolen, die im "umgebenden Kontext" einer Funktion (bekannt als ihre Umgebung ) definiert sind und sie zu einem GESCHLOSSENEN Ausdruck machen (dh zu einem Ausdruck, in dem jedes Symbol definiert ist und einen Wert hat, damit es ausgewertet werden kann).
Zum Beispiel, wenn Sie eine JavaScript-Funktion haben:
Es ist ein geschlossener Ausdruck, da alle darin vorkommenden Symbole darin definiert sind (ihre Bedeutung ist klar), sodass Sie ihn auswerten können. Mit anderen Worten, es ist in sich geschlossen .
Aber wenn Sie eine Funktion wie diese haben:
Es ist ein offener Ausdruck, weil es Symbole enthält, die darin nicht definiert wurden. Nämlich ,
y
. Wenn wir uns diese Funktion ansehen, können wir nicht sagen, was siey
ist und was sie bedeutet. Wir kennen ihren Wert nicht und können diesen Ausdruck daher nicht bewerten. Das heißt, wir können diese Funktion erst aufrufen, wenn wir sagen, wasy
darin bedeuten soll. Diesy
wird als freie Variable bezeichnet .Dies
y
erfordert eine Definition, aber diese Definition ist nicht Teil der Funktion - sie wird an einer anderen Stelle in ihrem "umgebenden Kontext" (auch als Umgebung bezeichnet ) definiert. Zumindest hoffen wir darauf: P.Zum Beispiel könnte es global definiert werden:
Oder es könnte in einer Funktion definiert werden, die es umschließt:
Der Teil der Umgebung, der den freien Variablen in einem Ausdruck ihre Bedeutung gibt, ist der Abschluss . Es wird so genannt, weil es einen offenen Ausdruck in einen geschlossenen Ausdruck verwandelt , indem diese fehlenden Definitionen für alle seine freien Variablen angegeben werden , damit wir ihn bewerten können.
In dem obigen Beispiel ist die innere Funktion (die wir keinen Namen gegeben haben , weil wir es nicht brauchten) ist ein offener Ausdruck , da die Variable
y
darin ist frei - seine Definition außerhalb der Funktion, in der Funktion , die es hüllt . Die Umgebung für diese anonyme Funktion ist die Menge der Variablen:Der Abschluss ist nun der Teil dieser Umgebung, der die innere Funktion schließt, indem er die Definitionen für alle seine freien Variablen bereitstellt . In unserem Fall war die einzige freie Variable in der inneren Funktion
y
, daher ist der Abschluss dieser Funktion diese Teilmenge ihrer Umgebung:Die beiden anderen in der Umgebung definierten Symbole sind nicht Teil des Abschlusses dieser Funktion, da sie nicht ausgeführt werden müssen. Sie werden nicht benötigt, um es zu schließen .
Mehr zur Theorie dahinter hier: https://stackoverflow.com/a/36878651/434562
Es ist zu beachten, dass im obigen Beispiel die Wrapper-Funktion ihre innere Funktion als Wert zurückgibt. Der Moment, in dem wir diese Funktion aufrufen, kann zeitlich von dem Moment entfernt sein, an dem die Funktion definiert (oder erstellt) wurde. Insbesondere wird seine Wrapping-Funktion nicht mehr ausgeführt und seine Parameter, die sich auf dem Aufrufstapel befanden, sind nicht mehr vorhanden: P Dies stellt ein Problem dar, da die innere Funktion beim Aufrufen vorhanden sein muss
y
! Mit anderen Worten, es erfordert, dass die Variablen von ihrem Abschluss die Wrapper-Funktion irgendwie überleben und bei Bedarf da sind. Daher muss die innere Funktion eine Momentaufnahme dieser Variablen erstellen, die geschlossen werden, und sie für die spätere Verwendung an einem sicheren Ort speichern. (Irgendwo außerhalb des Aufrufstapels.)Aus diesem Grund wird der Begriff " Schließen" häufig als eine spezielle Art von Funktion verwechselt, mit der solche Schnappschüsse der von ihnen verwendeten externen Variablen oder der Datenstruktur, in der diese Variablen für später gespeichert werden, erstellt werden können. Aber ich hoffe, Sie verstehen jetzt, dass sie nicht der Abschluss selbst sind - sie sind nur Möglichkeiten, Abschlüsse in einer Programmiersprache oder Sprachmechanismen zu implementieren , die es ermöglichen, dass die Variablen aus dem Abschluss der Funktion bei Bedarf vorhanden sind. Es gibt viele Missverständnisse in Bezug auf Schließungen, die dieses Thema (unnötigerweise) viel verwirrender und komplizierter machen, als es tatsächlich ist.
quelle
Ein Abschluss ist eine Funktion, die auf den Status einer anderen Funktion verweisen kann. In Python wird beispielsweise der Verschluss "inner" verwendet:
quelle
Um das Verständnis von Schließungen zu erleichtern, kann es hilfreich sein, zu untersuchen, wie sie in einer Verfahrenssprache implementiert werden können. Diese Erklärung folgt einer vereinfachten Implementierung von Schließungen in Schema.
Zunächst muss ich das Konzept eines Namespace einführen. Wenn Sie einen Befehl in einen Scheme-Interpreter eingeben, muss dieser die verschiedenen Symbole im Ausdruck auswerten und ihren Wert ermitteln. Beispiel:
Die Definitionsausdrücke speichern den Wert 3 an der Stelle für x und den Wert 4 an der Stelle für y. Wenn wir dann (+ xy) aufrufen, sucht der Interpreter nach den Werten im Namespace und kann die Operation ausführen und 7 zurückgeben.
In Schema gibt es jedoch Ausdrücke, mit denen Sie den Wert eines Symbols vorübergehend überschreiben können. Hier ist ein Beispiel:
Mit dem Schlüsselwort let wird ein neuer Namespace mit x als Wert 5 eingeführt. Sie werden feststellen, dass y immer noch 4 ist, sodass die zurückgegebene Summe 9 beträgt. Sie können dies auch sehen, wenn der Ausdruck x beendet hat ist wieder 3. In diesem Sinne wurde x vorübergehend durch den lokalen Wert maskiert.
Prozedurale und objektorientierte Sprachen haben ein ähnliches Konzept. Immer wenn Sie eine Variable in einer Funktion deklarieren, die denselben Namen wie eine globale Variable hat, erhalten Sie denselben Effekt.
Wie würden wir das umsetzen? Ein einfacher Weg ist mit einer verknüpften Liste - der Kopf enthält den neuen Wert und der Schwanz enthält den alten Namespace. Wenn Sie ein Symbol nachschlagen müssen, beginnen Sie am Kopf und arbeiten sich den Schwanz hinunter.
Kommen wir nun zur Implementierung erstklassiger Funktionen. Eine Funktion ist mehr oder weniger eine Reihe von Anweisungen, die ausgeführt werden müssen, wenn die Funktion aufgerufen wird und im Rückgabewert gipfelt. Wenn wir eine Funktion einlesen, können wir diese Anweisungen hinter den Kulissen speichern und beim Aufruf der Funktion ausführen.
Wir definieren x als 3 und plus-x als seinen Parameter y plus den Wert von x. Schließlich rufen wir plus-x in einer Umgebung auf, in der x durch ein neues x maskiert wurde, dieses mit dem Wert 5. Wenn wir lediglich die Operation (+ xy) für die Funktion plus-x speichern, da wir uns im Kontext befinden Wenn x 5 ist, wäre das zurückgegebene Ergebnis 9. Dies wird als dynamisches Scoping bezeichnet.
Scheme, Common Lisp und viele andere Sprachen haben jedoch das sogenannte lexikalische Scoping. Zusätzlich zum Speichern der Operation (+ xy) speichern wir den Namespace an diesem bestimmten Punkt. Auf diese Weise können wir beim Nachschlagen der Werte erkennen, dass x in diesem Zusammenhang wirklich 3 ist. Dies ist ein Abschluss.
Zusammenfassend können wir eine verknüpfte Liste verwenden, um den Status des Namespace zum Zeitpunkt der Funktionsdefinition zu speichern. Auf diese Weise können wir über einschließende Bereiche auf Variablen zugreifen und eine Variable lokal maskieren, ohne den Rest des Bereichs zu beeinflussen Programm.
quelle
Why do we want to access variables that are out of scope? when we say let x = 5, we want x to be 5 and not 3. What is happening?
Hier ist ein Beispiel aus der Praxis, warum Closures in den Arsch treten ... Dies stammt direkt aus meinem Javascript-Code. Lassen Sie mich veranschaulichen.
Und so würden Sie es verwenden:
Stellen Sie sich nun vor, Sie möchten, dass die Wiedergabe verzögert gestartet wird, z. B. 5 Sekunden später, nachdem dieses Code-Snippet ausgeführt wurde. Nun, das ist einfach
delay
und es ist ein Abschluss:Wenn Sie
delay
mit5000
ms aufrufen , wird das erste Snippet ausgeführt und die übergebenen Argumente in seinem Abschluss gespeichert. Dann, 5 Sekunden später, wenn dersetTimeout
Rückruf erfolgt, behält der Abschluss diese Variablen weiterhin bei, sodass die ursprüngliche Funktion mit den ursprünglichen Parametern aufgerufen werden kann.Dies ist eine Art Curry oder Funktionsdekoration.
Ohne Schließungen müssten Sie den Variablenstatus außerhalb der Funktion irgendwie beibehalten und so den Code außerhalb der Funktion mit etwas verunreinigen, das logisch dazu gehört. Die Verwendung von Verschlüssen kann die Qualität und Lesbarkeit Ihres Codes erheblich verbessern.
quelle
Funktionen, die keine freien Variablen enthalten, werden als reine Funktionen bezeichnet.
Funktionen, die eine oder mehrere freie Variablen enthalten, werden als Abschlüsse bezeichnet.
src: https://leanpub.com/javascriptallongesix/read#leanpub-auto-if-functions-without-free-variables-are-pure-are-closures-impure
quelle
tl; dr
Ein Abschluss ist eine Funktion und ihr Bereich wird einer Variablen zugewiesen (oder als solche verwendet). Daher der Namensabschluss: Der Bereich und die Funktion werden wie jede andere Entität eingeschlossen und verwendet.
Ausführliche Erklärung im Wikipedia-Stil
Laut Wikipedia ist eine Schließung :
Was bedeutet das? Schauen wir uns einige Definitionen an.
Ich werde Abschlüsse und andere verwandte Definitionen anhand dieses Beispiels erläutern:
Erstklassige Funktionen
Grundsätzlich bedeutet dies, dass wir Funktionen wie jede andere Entität verwenden können . Wir können sie ändern, als Argumente übergeben, von Funktionen zurückgeben oder Variablen zuweisen. Technisch gesehen sind sie erstklassige Bürger , daher der Name: erstklassige Funktionen.
In dem obigen Beispiel
startAt
gibt eine ( anonyme ) Funktion , die Funktion zugewiesen bekommenclosure1
undclosure2
. Wie Sie sehen, behandelt JavaScript Funktionen wie alle anderen Entitäten (erstklassige Bürger).Namensbindung
Bei der Namensbindung geht es darum herauszufinden, auf welche Daten eine Variable (Kennung) verweist . Der Umfang ist hier wirklich wichtig, da dies die Art und Weise bestimmt, wie eine Bindung aufgelöst wird.
Im obigen Beispiel:
y
ist gebunden3
.startAt
's Umfangx
ist an1
oder5
(abhängig von der Schließung) gebunden .Innerhalb des Bereichs der anonymen Funktion
x
ist sie an keinen Wert gebunden, daher muss sie in einem oberenstartAt
Bereich aufgelöst werden.Lexikalisches Scoping
Wie Wikipedia sagt , ist der Umfang:
Es gibt zwei Techniken:
Weitere Erklärungen finden Sie in dieser Frage und in Wikipedia .
Im obigen Beispiel können wir sehen, dass JavaScript einen lexikalischen Gültigkeitsbereich hat, da beim Auflösen
x
die Bindung im oberen Bereich (startAt
s) basierend auf dem Quellcode (die anonyme Funktion, die nach x sucht, ist darin definiertstartAt
) und gesucht wird nicht basierend auf dem Aufrufstapel, der Art (dem Bereich, in dem) die Funktion aufgerufen wurde.Einwickeln (Verschließen)
In unserem Beispiel , wenn wir anrufen
startAt
, wird es zurückgeben (First-Class) Funktion, die zugewiesen wirdclosure1
undclosure2
somit wird ein Verschluss geschaffen, weil die übergebenen Variablen1
und5
wird innerhalb gespeichert werdenstartAt
‚s Umfang, die mit dem zurück eingeschlossen werden anonyme Funktion. Wenn wir diese anonyme Funktion überclosure1
undclosure2
mit demselben Argument (3
) aufrufen , wird der Wert vony
sofort gefunden (da dies der Parameter dieser Funktion ist),x
ist jedoch nicht an den Bereich der anonymen Funktion gebunden, sodass die Auflösung in fortgesetzt wird der (lexikalisch) obere Funktionsumfang (der im Abschluss gespeichert wurde) wox
festgestellt wird, dass er an entweder1
oder gebunden ist5
. Jetzt wissen wir alles für die Summierung, sodass das Ergebnis zurückgegeben und dann gedruckt werden kann.Jetzt sollten Sie die Schließungen und ihr Verhalten verstehen, was ein wesentlicher Bestandteil von JavaScript ist.
Currying
Oh, und Sie haben auch gelernt, worum es beim Currying geht: Sie verwenden Funktionen (Abschlüsse), um jedes Argument einer Operation zu übergeben, anstatt eine Funktion mit mehreren Parametern zu verwenden.
quelle
Closure ist eine Funktion in JavaScript, bei der eine Funktion Zugriff auf ihre eigenen Bereichsvariablen, Zugriff auf die äußeren Funktionsvariablen und Zugriff auf die globalen Variablen hat.
Closure hat auch nach Rückkehr der äußeren Funktion Zugriff auf seinen äußeren Funktionsumfang. Dies bedeutet, dass ein Abschluss Variablen und Argumente seiner äußeren Funktion auch nach Beendigung der Funktion speichern und darauf zugreifen kann.
Die innere Funktion kann auf die Variablen zugreifen, die in ihrem eigenen Bereich, dem Bereich der äußeren Funktion und dem globalen Bereich definiert sind. Die äußere Funktion kann auf die in ihrem eigenen Bereich und im globalen Bereich definierte Variable zugreifen.
Beispiel für die Schließung :
Die Ausgabe ist 20, was die Summe der inneren Variablen der inneren Funktion, der äußeren Funktionsvariablen und des globalen Variablenwerts ist.
quelle
In einer normalen Situation sind Variablen an die Gültigkeitsbereichsregel gebunden: Lokale Variablen funktionieren nur innerhalb der definierten Funktion. Das Schließen ist eine Möglichkeit, diese Regel aus Bequemlichkeitsgründen vorübergehend zu brechen.
im obigen Code
lambda(|n| a_thing * n}
ist der Abschluss, weila_thing
vom Lambda (einem anonymen Funktionsersteller) verwiesen wird.Wenn Sie nun die resultierende anonyme Funktion in eine Funktionsvariable einfügen.
foo verstößt gegen die normale Scoping-Regel und verwendet intern 4.
gibt 12 zurück.
quelle
Kurz gesagt, der Funktionszeiger ist nur ein Zeiger auf eine Stelle in der Programmcodebasis (wie der Programmzähler). Während Closure = Funktionszeiger + Stapelrahmen .
.
quelle
• Ein Abschluss ist ein Unterprogramm und die Referenzierungsumgebung, in der es definiert wurde
- Die Referenzierungsumgebung wird benötigt, wenn das Unterprogramm von einer beliebigen Stelle im Programm aus aufgerufen werden kann
- Eine Sprache mit statischem Gültigkeitsbereich, die keine verschachtelten Unterprogramme zulässt, benötigt keine Abschlüsse
- Abschlüsse sind nur erforderlich, wenn ein Unterprogramm auf Variablen in Verschachtelungsbereichen zugreifen kann und von überall aufgerufen werden kann
- Um das Schließen zu unterstützen, muss eine Implementierung möglicherweise für einige Variablen einen unbegrenzten Umfang bereitstellen (da ein Unterprogramm möglicherweise auf eine nicht lokale Variable zugreift, die normalerweise nicht mehr aktiv ist).
Beispiel
quelle
Hier ist ein weiteres Beispiel aus dem wirklichen Leben und die Verwendung einer in Spielen beliebten Skriptsprache - Lua. Ich musste die Funktionsweise einer Bibliotheksfunktion leicht ändern, um ein Problem zu vermeiden, bei dem stdin nicht verfügbar war.
Der Wert von old_dofile verschwindet, wenn dieser Codeblock seinen Gültigkeitsbereich beendet (weil er lokal ist). Der Wert wurde jedoch in einen Abschluss eingeschlossen, sodass die neu definierte Dofile-Funktion darauf zugreifen kann, oder vielmehr eine Kopie, die zusammen mit der Funktion als gespeichert wird 'upvalue'.
quelle
Von Lua.org :
quelle
Wenn Sie aus der Java-Welt stammen, können Sie einen Abschluss mit einer Mitgliedsfunktion einer Klasse vergleichen. Schauen Sie sich dieses Beispiel an
Die Funktion
g
ist ein Abschluss:g
schließt sicha
an.g
Kann also mit einer Elementfunktion verglichen werden,a
kann mit einem Klassenfeld verglichen werden und die Funktionf
mit einer Klasse.quelle
Closures Immer wenn eine Funktion in einer anderen Funktion definiert ist, hat die innere Funktion Zugriff auf die in der äußeren Funktion deklarierten Variablen. Abschlüsse lassen sich am besten anhand von Beispielen erklären. In Listing 2-18 sehen Sie, dass die innere Funktion vom äußeren Bereich aus auf eine Variable (variableInOuterFunction) zugreifen kann. Die Variablen in der äußeren Funktion wurden durch die innere Funktion geschlossen (oder in diese gebunden). Daher der Begriff Schließung. Das Konzept an sich ist einfach genug und ziemlich intuitiv.
Quelle: http://index-of.es/Varios/Basarat%20Ali%20Syed%20(auth.)-Beginning%20Node.js-Apress%20(2014).pdf
quelle
Bitte schauen Sie unter den Code, um die Schließung genauer zu verstehen:
Was wird hier ausgegeben?
0,1,2,3,4
nicht das wird5,5,5,5,5
wegen der Schließung seinWie wird es sich lösen? Die Antwort ist unten:
Lassen Sie mich einfach erklären, wann eine erstellte Funktion nichts passiert, bis sie im ersten Code, der 5-mal aufgerufen wurde, eine solche for-Schleife aufgerufen hat, aber nicht sofort, wenn sie aufgerufen hat, dh nach 1 Sekunde, und auch dies ist asynchron, also bevor diese for-Schleife beendet ist, und speichern Sie den Wert 5 in var i und schließlich ausführen
setTimeout
Funktion fünfmal und drucken5,5,5,5,5
Hier erfahren Sie, wie Sie mit IIFE lösen können, dh mit dem sofortigen Aufrufen des Funktionsausdrucks
Weitere Informationen finden Sie im Ausführungskontext, um das Schließen zu verstehen.
Es gibt noch eine weitere Lösung, um dies mit let (ES6-Funktion) zu lösen, aber unter der Haube oben funktioniert die Funktion
=> Weitere Erklärung:
Im Speicher, wenn für Schleife Bild ausführen, machen Sie wie folgt:
Schleife 1)
Schleife 2)
Schleife 3)
Schleife 4)
Schleife 5)
Hier wird i nicht ausgeführt, und nach der vollständigen Schleife hat var i den Wert 5 im Speicher gespeichert, aber sein Gültigkeitsbereich ist immer in der untergeordneten Funktion sichtbar. Wenn die Funktion also
setTimeout
fünf Mal von innen nach außen ausgeführt wird , wird sie gedruckt5,5,5,5,5
Um dieses Problem zu beheben, verwenden Sie IIFE wie oben erläutert.
quelle
Currying: Sie können eine Funktion teilweise bewerten, indem Sie nur eine Teilmenge ihrer Argumente übergeben. Bedenken Sie:
Closure: Ein Closure ist nichts anderes als der Zugriff auf eine Variable außerhalb des Funktionsumfangs. Es ist wichtig zu beachten, dass eine Funktion innerhalb einer Funktion oder eine verschachtelte Funktion kein Abschluss ist. Abschlüsse werden immer verwendet, wenn auf Variablen außerhalb des Funktionsumfangs zugegriffen werden muss.
quelle
Das Schließen ist sehr einfach. Wir können es wie folgt betrachten: Closure = Funktion + seine lexikalische Umgebung
Betrachten Sie die folgende Funktion:
Was wird die Schließung im obigen Fall sein? Funktion init () und Variablen in ihrer lexikalischen Umgebung, dh Name. Closure = init () + name
Betrachten Sie eine andere Funktion:
Was werden die Schließungen hier sein? Die innere Funktion kann auf Variablen der äußeren Funktion zugreifen. displayName () kann auf den in der übergeordneten Funktion init () deklarierten Variablennamen zugreifen. Es werden jedoch dieselben lokalen Variablen in displayName () verwendet, wenn sie vorhanden sind.
Abschluss 1: Init-Funktion + (Namensvariable + DisplayName () -Funktion) -> lexikalischer Bereich
Abschluss 2: displayName-Funktion + (Namensvariable) -> lexikalischer Bereich
quelle
Durch Verschlüsse wird JavaScript mit dem Status versehen.
Zustand in der Programmierung bedeutet einfach, sich an Dinge zu erinnern.
Beispiel
Im obigen Fall wird state in der Variablen "a" gespeichert. Wir folgen, indem wir mehrmals 1 zu "a" hinzufügen. Das können wir nur, weil wir uns an den Wert "erinnern" können. Der Staatsinhaber "a" speichert diesen Wert im Speicher.
In Programmiersprachen möchten Sie häufig den Überblick behalten, sich Informationen merken und zu einem späteren Zeitpunkt darauf zugreifen.
Dies wird in anderen Sprachen üblicherweise durch die Verwendung von Klassen erreicht. Eine Klasse verfolgt genau wie Variablen ihren Status. Und Instanzen dieser Klasse haben wiederum auch einen Status in sich. Status bedeutet einfach Informationen, die Sie später speichern und abrufen können.
Beispiel
Wie können wir über die "Render" -Methode auf "weight" zugreifen? Nun, danke an den Staat. Jede Instanz der Klasse Bread kann ihr eigenes Gewicht rendern, indem sie es aus dem "Zustand" liest, einem Ort im Speicher, an dem wir diese Informationen speichern können.
Jetzt ist JavaScript eine sehr einzigartige Sprache, die historisch gesehen keine Klassen hat (jetzt, aber unter der Haube gibt es nur Funktionen und Variablen), sodass Closures JavaScript die Möglichkeit bieten, sich Dinge zu merken und später darauf zuzugreifen.
Beispiel
Das obige Beispiel hat das Ziel erreicht, den Zustand mit einer Variablen beizubehalten. Das ist toll! Dies hat jedoch den Nachteil, dass die Variable (der "Staats" -Inhaber) jetzt verfügbar ist. Wir können es besser machen. Wir können Verschlüsse verwenden.
Beispiel
Das ist fantastisch.
Jetzt kann unsere "Zähl" -Funktion zählen. Dies ist nur möglich, weil es den Status "halten" kann. Der Zustand ist in diesem Fall die Variable "n". Diese Variable ist jetzt geschlossen. Zeitlich und räumlich geschlossen. Mit der Zeit, weil Sie es nie wieder herstellen, ändern, ihm einen Wert zuweisen oder direkt damit interagieren können. Im Weltraum, weil es geografisch in der Funktion "countGenerator" verschachtelt ist.
Warum ist das fantastisch? Denn ohne ein anderes ausgeklügeltes und kompliziertes Werkzeug (z. B. Klassen, Methoden, Instanzen usw.) können wir 1. die Kontrolle aus der Ferne verbergen
Wir verbergen den Zustand, die Variable "n", was ihn zu einer privaten Variablen macht! Wir haben auch eine API erstellt, die diese Variable auf vordefinierte Weise steuern kann. Insbesondere können wir die API wie folgt aufrufen: "count ()" und das addiert 1 zu "n" aus einer "Entfernung". In keiner Weise, Form oder Gestalt kann jemals jemand über die API auf "n" zugreifen.
JavaScript ist in seiner Einfachheit wirklich erstaunlich.
Verschlüsse sind ein großer Teil dessen, warum dies so ist.
quelle
Ein einfaches Beispiel in Groovy als Referenz:
quelle