Frage

Ich habe eine Frage zum Thema Currying gestellt und Schließungen wurden erwähnt.Was ist eine Schließung?Was hat das mit Curry zu tun?

War es hilfreich?

Lösung

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 dem Sie sie deklarieren.

function() {
  var a = 1;
  console.log(a); // works
}    
console.log(a); // fails

Wenn ich versuche, auf eine lokale Variable zuzugreifen, suchen die meisten Sprachen im aktuellen Bereich danach und dann in den übergeordneten Bereichen, bis sie den Stammbereich erreichen.

var a = 1;
function() {
  console.log(a); // works
}    
console.log(a); // works

Wenn ein Block oder eine Funktion nicht mehr benötigt wird, werden die lokalen Variablen nicht mehr benötigt und gehen normalerweise nicht mehr aus dem Speicher hervor.

So erwarten wir normalerweise, dass die Dinge funktionieren.

Ein Abschluss ist ein persistenter lokaler Variablenbereich

Ein Abschluss ist ein persistenter Bereich, der lokale Variablen auch dann beibehält, wenn die Codeausführung diesen Block verlassen hat.Sprachen, die das Schließen unterstützen (z. B. JavaScript, Swift und Ruby), ermöglichen es Ihnen, einen Verweis auf einen Bereich (einschließlich seiner übergeordneten Bereiche) beizubehalten, selbst nachdem die Ausführung des Blocks, in dem diese Variablen deklariert wurden, abgeschlossen ist, vorausgesetzt, Sie behalten einen Verweis zu diesem Block oder dieser Funktion irgendwo.

Das Gültigkeitsbereichsobjekt und alle seine lokalen Variablen sind an die Funktion gebunden und bleiben bestehen, solange diese Funktion besteht.

Dies gibt uns Funktionsportabilität.Wir können davon ausgehen, dass alle Variablen, die sich bei der ersten Definition der Funktion im Gültigkeitsbereich befanden, auch dann noch im Gültigkeitsbereich sind, wenn wir die Funktion später aufrufen, selbst wenn wir die Funktion in einem völlig anderen Kontext aufrufen.

Zum Beispiel

Hier ist ein wirklich einfaches Beispiel in JavaScript, das den Punkt verdeutlicht:

outer = function() {
  var a = 1;
  var inner = function() {
    console.log(a);
  }
  return inner; // this returns a function
}

var fnc = outer(); // execute outer to get inner 
fnc();

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 Variable a liegt im Rahmen der inneren Funktion.

Wenn eine Funktion beendet wird, werden normalerweise alle ihre lokalen Variablen gelöscht.Wenn wir jedoch die innere Funktion zurückgeben und sie einer Variablen zuweisen fnc damit es danach bestehen bleibt outer ist ausgestiegen, alle Variablen, die wann im Gültigkeitsbereich waren inner wurde definiert, auch bestehen bleiben.Die Variable a wurde geschlossen – es befindet sich innerhalb einer Schließung.

Beachten Sie, dass die Variable a ist völlig privat fnc.Dies ist eine Möglichkeit, private Variablen in einer funktionalen Programmiersprache wie JavaScript zu erstellen.

Wie Sie vielleicht erraten können, wenn ich anrufe fnc() Es gibt den Wert von aus a, also „1“.

In einer Sprache ohne Abschluss die Variable a wäre bei der Funktion Müll gesammelt und weggeworfen worden outer verlassen.Der Aufruf von fnc hätte einen Fehler ausgelöst, weil a existiert nicht mehr.

In JavaScript die Variable a bleibt bestehen, da der Variablenbereich erstellt wird, wenn die Funktion zum ersten Mal deklariert wird, und bleibt bestehen, solange die Funktion weiterhin existiert.

a gehört zum Geltungsbereich von outer.Der Umfang von inner hat einen übergeordneten Zeiger auf den Bereich von outer. fnc ist eine Variable, die darauf zeigt inner. a bleibt so lange bestehen fnc besteht fort. a liegt innerhalb der Schließung.

Andere Tipps

Ich gebe ein Beispiel (in JavaScript):

function makeCounter () {
  var count = 0;
  return function () {
    count += 1;
    return count;
  }
}

var x = makeCounter();

x(); returns 1

x(); returns 2

...etc...

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.Basierend auf dem sogenannten lexikalischen Scoping weiß es, wo es zu finden ist – es muss an der Stelle suchen, an der es definiert ist, um den Wert zu finden.Dieser „verborgene“ Wert wird als Abschluss bezeichnet.

Hier noch einmal mein Curry-Beispiel:

function add (a) {
  return function (b) {
    return a + b;
  }
}

var add3 = add(3);

add3(4); returns 7

Was Sie sehen können, ist, dass, wenn Sie add mit dem Parameter a (der 3 ist) aufrufen, dieser Wert im Abschluss der zurückgegebenen Funktion enthalten ist, die wir als add3 definieren.Auf diese Weise weiß es beim Aufruf von add3, wo sich der a-Wert zum Durchführen der Addition befindet.

Kyles Antwort ist ziemlich gut.Ich denke, die einzige zusätzliche Klarstellung besteht darin, dass der Abschluss im Grunde eine Momentaufnahme des Stapels zum Zeitpunkt der Erstellung der Lambda-Funktion ist.Wenn die Funktion dann erneut ausgeführt wird, wird der Stapel in den Zustand vor der Ausführung der Funktion zurückversetzt.Wie Kyle erwähnt, ist dieser verborgene Wert (count) ist verfügbar, wenn die Lambda-Funktion ausgeführt wird.

Erstens: Im Gegensatz zu dem, was die meisten Leute hier sagen, Schließung ist nicht eine Funktion!Na und Ist Es?
es ist ein Satz von Symbolen, die im „umgebenden Kontext“ einer Funktion (bekannt als „its“) definiert sind Umfeld), die es zu einem CLOSED-Ausdruck machen (d. h. zu einem Ausdruck, in dem jedes Symbol definiert ist und einen Wert hat, sodass es ausgewertet werden kann).

Wenn Sie beispielsweise eine JavaScript-Funktion haben:

function closed(x) {
  return x + 3;
}

es ist ein geschlossener Ausdruck weil alle darin vorkommenden Symbole darin definiert sind (ihre Bedeutung ist klar), sodass Sie es bewerten können.Mit anderen Worten, das ist es in sich geschlossen.

Aber wenn Sie eine Funktion wie diese haben:

function open(x) {
  return x*y + 3;
}

es ist ein offener Ausdruck weil es darin Symbole gibt, die darin nicht definiert wurden.Nämlich, y.Wenn wir uns diese Funktion ansehen, können wir nicht sagen, was y ist und was es bedeutet, wir kennen seinen Wert nicht, daher können wir diesen Ausdruck nicht bewerten.D.h.Wir können diese Funktion nicht aufrufen, bis wir sagen, was y soll darin bedeuten.Das y heißt a freie Variable.

Das y schreit nach einer Definition, aber diese Definition ist nicht Teil der Funktion – sie wird woanders definiert, in ihrem „umgebenden Kontext“ (auch bekannt als Umfeld).Zumindest hoffen wir das :P

Es könnte beispielsweise global definiert werden:

var y = 7;

function open(x) {
  return x*y + 3;
}

Oder es könnte in einer Funktion definiert werden, die es umschließt:

var global = 2;

function wrapper(y) {
   var w = "unused";

   return function(x) {
     return x*y + 3;
   }

}

Der Teil der Umgebung, der den freien Variablen in einem Ausdruck ihre Bedeutung gibt, ist der Schließung.Es wird so genannt, weil es sich um ein dreht offen Ausdruck in a geschlossen Erstens, indem diese fehlenden Definitionen für alle bereitgestellt werden freie Variablen, damit wir es bewerten konnten.

Im obigen Beispiel ist die innere Funktion (der wir keinen Namen gegeben haben, weil wir sie nicht brauchten) eine offener Ausdruck weil die Variable y darin ist frei – seine Definition liegt außerhalb der Funktion, in der Funktion, die sie umschließt.Der Umfeld für diese anonyme Funktion ist die Menge der Variablen:

{
  global: 2,
  w: "unused",
  y: [whatever has been passed to that wrapper function as its parameter `y`]
}

Jetzt die Schließung ist der Teil dieser Umgebung, der schließt die innere Funktion, indem sie die Definitionen für alle ihre Funktionen bereitstellt freie Variablen.In unserem Fall war die einzige freie Variable in der inneren Funktion y, also ist der Abschluss dieser Funktion diese Teilmenge ihrer Umgebung:

{
  y: [whatever has been passed to that wrapper function as its parameter `y`]
}

Die anderen beiden in der Umgebung definierten Symbole sind nicht Teil der Schließung dieser Funktion, da diese nicht ausgeführt werden muss.Das ist nicht nötig schließen Es.

Mehr zur Theorie dahinter hier:https://stackoverflow.com/a/36878651/434562

Es ist erwähnenswert, dass im obigen Beispiel die Wrapper-Funktion ihre innere Funktion als Wert zurückgibt.Der Zeitpunkt, an dem wir diese Funktion aufrufen, kann zeitlich weit von dem Zeitpunkt entfernt liegen, an dem die Funktion definiert (oder erstellt) wurde.Insbesondere läuft seine Wrapping-Funktion nicht mehr und seine Parameter, die sich auf dem Aufrufstapel befanden, sind nicht mehr vorhanden :P Das stellt ein Problem dar, weil die innere Funktion benötigt wird y Da sein, wenn es heißt!Mit anderen Worten: Es benötigt die Variablen von seinem Abschluss bis irgendwie überleben die Wrapper-Funktion und sind bei Bedarf da.Daher muss die innere Funktion a Schnappschuss dieser Variablen, die den Abschluss vornehmen, und speichert sie an einem sicheren Ort für die spätere Verwendung.(Irgendwo außerhalb des Aufrufstapels.)

Und deshalb wird der Begriff oft verwechselt Schließung Dabei handelt es sich um einen speziellen Funktionstyp, der solche Snapshots der von ihnen verwendeten externen Variablen oder der Datenstruktur erstellen kann, die zum Speichern dieser Variablen für später verwendet wird.Aber ich hoffe, Sie verstehen jetzt, dass dies der Fall ist nicht die Schließung selbst – sie sind nur Wege dazu implementieren Abschlüsse in einer Programmiersprache oder Sprachmechanismen, die es ermöglichen, dass die Variablen aus dem Abschluss der Funktion bei Bedarf vorhanden sind.Es gibt viele Missverständnisse rund um Abschlüsse, die dieses Thema (unnötigerweise) viel verwirrender und komplizierter machen, als es tatsächlich ist.

Ein Abschluss ist eine Funktion, die auf den Zustand einer anderen Funktion verweisen kann.In Python wird hierfür beispielsweise der Abschluss „inner“ verwendet:

def outer (a):
    b = "variable in outer()"
    def inner (c):
        print a, b, c
    return inner

# Now the return value from outer() can be saved for later
func = outer ("test")
func (1) # prints "test variable in outer() 1

Um das Verständnis von Abschlüssen zu erleichtern, könnte es nützlich sein, zu untersuchen, wie sie in einer prozeduralen Sprache implementiert werden könnten.Diese Erklärung folgt einer vereinfachten Implementierung von Abschlüssen in Scheme.

Zunächst muss ich das Konzept eines Namespace vorstellen.Wenn Sie einen Befehl in einen Scheme-Interpreter eingeben, muss dieser die verschiedenen Symbole im Ausdruck auswerten und ihren Wert ermitteln.Beispiel:

(define x 3)

(define y 4)

(+ x y) returns 7

Die definierten Ausdrücke speichern den Wert 3 an der Stelle für x und den Wert 4 an der Stelle für y.Wenn wir dann (+ x y) aufrufen, sucht der Interpreter nach den Werten im Namespace und kann die Operation ausführen und 7 zurückgeben.

Allerdings gibt es in Scheme Ausdrücke, mit denen Sie den Wert eines Symbols vorübergehend überschreiben können.Hier ist ein Beispiel:

(define x 3)

(define y 4)

(let ((x 5))
   (+ x y)) returns 9

x returns 3

Das Schlüsselwort let führt einen neuen Namespace mit x als Wert 5 ein.Sie werden feststellen, dass y immer noch 4 ist, sodass die zurückgegebene Summe 9 beträgt.Sie können auch sehen, dass x nach Beendigung des Ausdrucks wieder 3 ist.In diesem Sinne wurde x vorübergehend durch den lokalen Wert maskiert.

Prozedurale und objektorientierte Sprachen haben ein ähnliches Konzept.Immer wenn Sie in einer Funktion eine Variable deklarieren, die denselben Namen wie eine globale Variable hat, erzielen Sie den gleichen Effekt.

Wie würden wir das umsetzen?Eine einfache Möglichkeit ist eine verknüpfte 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 nach unten vor.

Kommen wir nun zunächst zur Implementierung erstklassiger Funktionen.Mehr oder weniger handelt es sich bei einer Funktion um eine Reihe von Anweisungen, die beim Aufruf der Funktion ausgeführt werden müssen und deren Ergebnis im Rückgabewert gipfelt.Wenn wir eine Funktion einlesen, können wir diese Anweisungen im Hintergrund speichern und beim Aufruf der Funktion ausführen.

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns ?

Wir definieren x als 3 und plus-x als seinen Parameter y plus dem 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 (+ x y) für die Funktion plus-x speichern, wäre das zurückgegebene Ergebnis 9, da wir uns im Kontext befinden, in dem x 5 ist.Dies wird als dynamisches Scoping bezeichnet.

Scheme, Common Lisp und viele andere Sprachen verfügen jedoch über das sogenannte lexikalische Scoping – zusätzlich zum Speichern der Operation (+ x y) speichern wir auch den Namespace an diesem bestimmten Punkt.Auf diese Weise können wir beim Nachschlagen der Werte erkennen, dass x in diesem Zusammenhang tatsächlich 3 ist.Das ist eine Schließung.

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns 7

Zusammenfassend lässt sich sagen, dass wir eine verknüpfte Liste verwenden können, um den Status des Namespace zum Zeitpunkt der Funktionsdefinition zu speichern. Dies ermöglicht uns den Zugriff auf Variablen aus umschließenden Bereichen und bietet uns außerdem die Möglichkeit, eine Variable lokal zu maskieren, ohne den Rest davon zu beeinflussen Programm.

Hier ist ein Beispiel aus der Praxis, warum Verschlüsse der Hammer sind ...Das ist direkt aus meinem Javascript-Code.Lassen Sie mich das veranschaulichen.

Function.prototype.delay = function(ms /*[, arg...]*/) {
  var fn = this,
      args = Array.prototype.slice.call(arguments, 1);

  return window.setTimeout(function() {
      return fn.apply(fn, args);
  }, ms);
};

Und so würden Sie es verwenden:

var startPlayback = function(track) {
  Player.play(track);  
};
startPlayback(someTrack);

Stellen Sie sich nun vor, Sie möchten, dass die Wiedergabe verzögert startet, beispielsweise 5 Sekunden später, nachdem dieses Code-Snippet ausgeführt wurde.Nun, das ist ganz einfach delay und es ist Schluss:

startPlayback.delay(5000, someTrack);
// Keep going, do other things

Wenn du anrufst delay mit 5000ms, das erste Snippet wird ausgeführt und speichert die übergebenen Argumente in seinem Abschluss.Dann, 5 Sekunden später, als die setTimeout Wenn ein Rückruf erfolgt, behält der Abschluss diese Variablen weiterhin bei, sodass er die ursprüngliche Funktion mit den ursprünglichen Parametern aufrufen kann.
Dies ist eine Art Curry- oder Funktionsdekoration.

Ohne Abschlüsse müssten Sie den Status dieser Variablen außerhalb der Funktion irgendwie aufrechterhalten und so den Code außerhalb der Funktion mit etwas verunreinigen, das logisch dazu gehört.Die Verwendung von Abschlüssen kann die Qualität und Lesbarkeit Ihres Codes erheblich verbessern.

tl;dr

Ein Abschluss ist eine Funktion und ihr Gültigkeitsbereich wird einer Variablen zugewiesen (oder als solche verwendet).Somit lautet der Namensabschluss:Der Umfang und die Funktion werden eingeschlossen und wie jede andere Entität verwendet.

Ausführliche Erklärung im Wikipedia-Stil

Laut Wikipedia eine Schließung Ist:

Techniken zur Implementierung lexikalischer Namensbindung in Sprachen mit erstklassigen Funktionen.

Was bedeutet das?Schauen wir uns einige Definitionen an.

Ich werde Abschlüsse und andere verwandte Definitionen anhand dieses Beispiels erläutern:

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

var closure1 = startAt(1);
var closure2 = startAt(5);

console.log(closure1(3)); // 4 (x == 1, y == 3)
console.log(closure2(3)); // 8 (x == 5, y == 3)

Erstklassige Funktionen

Im Grunde bedeutet das Wir können Funktionen wie jede andere Entität verwenden.Wir können sie ändern, als Argumente übergeben, von Funktionen zurückgeben oder Variablen zuweisen.Technisch gesehen sind sie es erstklassige Bürger, daher der Name:erstklassige Funktionen.

Im Beispiel oben: startAt gibt ein (anonym) Funktion, der die Funktion zugewiesen wird closure1 Und closure2.Wie Sie sehen, behandelt JavaScript Funktionen wie alle anderen Entitäten (erstklassige Bürger).

Namensbindung

Namensbindung geht es darum, es herauszufinden welche Daten eine Variable (Bezeichner) Verweise.Der Umfang ist hier wirklich wichtig, da er bestimmt, wie eine Bindung gelöst wird.

Im Beispiel oben:

  • Im Bereich der inneren anonymen Funktion y ist gebunden an 3.
  • In startAtUmfang, x ist gebunden an 1 oder 5 (abhängig von der Schließung).

Innerhalb des Geltungsbereichs der anonymen Funktion x ist an keinen Wert gebunden und muss daher in einem oberen (startAt's) Umfang.

Lexikalisches Scoping

Als Wikipedia sagt, der Anwendungsbereich:

Ist der Bereich eines Computerprogramms, in dem die Bindung gültig ist: wobei der Name verwendet werden kann, um auf die Entität zu verweisen.

Es gibt zwei Techniken:

  • Lexikalisches (statisches) Scoping:Die Definition einer Variablen wird aufgelöst, indem der enthaltende Block oder die enthaltende Funktion durchsucht wird. Wenn dies fehlschlägt, wird der äußere enthaltende Block durchsucht usw.
  • Dynamisches Scoping:Es wird nach der aufrufenden Funktion gesucht, dann nach der Funktion, die diese aufrufende Funktion aufgerufen hat, und so weiter, wobei der Aufrufstapel nach oben verschoben wird.

Für weitere Erklärungen: Schauen Sie sich diese Frage an Und schau mal bei Wikipedia vorbei.

Im obigen Beispiel können wir sehen, dass JavaScript einen lexikalischen Gültigkeitsbereich hat, denn wann x gelöst ist, wird die Bindung im oberen Bereich gesucht (startAt's) Gültigkeitsbereich, basierend auf dem Quellcode (die anonyme Funktion, die nach x sucht, ist darin definiert). startAt) und nicht basierend auf dem Aufrufstapel, der Art und Weise (dem Bereich, in dem) die Funktion aufgerufen wurde.

Einpacken (Abschließen).

In unserem Beispiel, wenn wir anrufen startAt, wird eine (erstklassige) Funktion zurückgegeben, der zugewiesen wird closure1 Und closure2 Somit wird ein Abschluss erstellt, da die Variablen übergeben werden 1 Und 5 wird darin gespeichert startAtDer Gültigkeitsbereich wird in die zurückgegebene anonyme Funktion eingeschlossen.Wenn wir diese anonyme Funktion über aufrufen closure1 Und closure2 mit dem gleichen Argument (3), der Wert von y wird sofort gefunden (da das der Parameter dieser Funktion ist), aber x ist nicht an den Gültigkeitsbereich der anonymen Funktion gebunden, sodass die Auflösung im (lexikalisch) oberen Funktionsbereich (der im Abschluss gespeichert wurde) fortgesetzt wird x Es wird festgestellt, dass es an eines von beiden gebunden ist 1 oder 5.Jetzt wissen wir alles für die Summierung, sodass das Ergebnis zurückgegeben und dann gedruckt werden kann.

Jetzt sollten Sie Abschlüsse und deren Verhalten verstehen, was ein grundlegender Bestandteil von JavaScript ist.

Curry

Oh, und du hast auch was gelernt Curry handelt von:Sie verwenden Funktionen (Abschlüsse), um jedes Argument einer Operation zu übergeben, anstatt eine Funktion mit mehreren Parametern zu verwenden.

Funktionen, die keine freien Variablen enthalten, werden reine Funktionen genannt.

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

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

var foo = "bar"

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

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

In einer normalen Situation sind Variablen durch die Gültigkeitsbereichsregel gebunden:Lokale Variablen funktionieren nur innerhalb der definierten Funktion.Die Schließung ist eine Möglichkeit, diese Regel aus Bequemlichkeitsgründen vorübergehend zu brechen.

def n_times(a_thing)
  return lambda{|n| a_thing * n}
end

im obigen Code, lambda(|n| a_thing * n} ist die Schließung denn a_thing wird vom Lambda (einem anonymen Funktionsersteller) referenziert.

Wenn Sie nun die resultierende anonyme Funktion in eine Funktionsvariable einfügen.

foo = n_times(4)

foo bricht die normale Scoping-Regel und beginnt intern mit der Verwendung von 4.

foo.call(3)

gibt 12 zurück.

Kurz gesagt ist ein Funktionszeiger nur ein Zeiger auf eine Stelle in der Programmcodebasis (wie ein Programmzähler).Wohingegen Abschluss = Funktionszeiger + Stapelrahmen.

.

Schließung 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.

Der Abschluss hat Zugriff auf seinen äußeren Funktionsumfang, auch nachdem die äußere Funktion zurückgekehrt ist.Dies bedeutet, dass sich ein Abschluss Variablen und Argumente seiner äußeren Funktion merken und darauf zugreifen kann, selbst nachdem die Funktion beendet wurde.

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.Und die äußere Funktion kann auf die in ihrem eigenen Bereich und im globalen Bereich definierte Variable zugreifen.

******************
Example of Closure
******************

var globalValue = 5;

function functOuter() 
{
    var outerFunctionValue = 10;

    //Inner function has access to the outer function value
    //and the global variables
    function functInner() 
    {
        var innerFunctionValue = 5;
        alert(globalValue+outerFunctionValue + innerFunctionValue);
    }
    functInner();
}
functOuter();

Die Ausgabe beträgt 20, also die Summe aus der eigenen Variablen der inneren Funktion, der Variablen der äußeren Funktion und dem Wert der globalen Variablen.

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 mit der Nichtverfügbarkeit von stdin zu vermeiden.

local old_dofile = dofile

function dofile( filename )
  if filename == nil then
    error( 'Can not use default of stdin.' )
  end

  old_dofile( filename )
end

Der Wert von old_dofile verschwindet, wenn dieser Codeblock seinen Gültigkeitsbereich abschließt (da er lokal ist), der Wert wurde jedoch in einen Abschluss eingeschlossen, sodass die neu definierte Dofile-Funktion darauf zugreifen KANN, oder vielmehr auf eine Kopie, die zusammen mit der Funktion als gespeichert wird „Aufwertung“.

Aus Lua.org:

Wenn eine Funktion in eine andere Funktion eingeschlossen geschrieben wird, hat sie vollen Zugriff auf lokale Variablen der umschließenden Funktion.Diese Funktion wird als lexikalisches Scoping bezeichnet.Auch wenn das offensichtlich klingt, ist es das nicht.Lexikalisches Scoping und erstklassige Funktionen sind ein leistungsstarkes Konzept in einer Programmiersprache, aber nur wenige Sprachen unterstützen dieses Konzept.

Wenn Sie aus der Java-Welt stammen, können Sie einen Abschluss mit einer Mitgliedsfunktion einer Klasse vergleichen.Schauen Sie sich dieses Beispiel an

var f=function(){
  var a=7;
  var g=function(){
    return a;
  }
  return g;
}

Die Funktion g ist ein Abschluss: g schließt a In.Also g kann mit einer Mitgliedsfunktion verglichen werden, a kann mit einem Klassenfeld und der Funktion verglichen werden f mit einer Klasse.

Schließungen Wenn wir in einer anderen Funktion eine Funktion definiert haben, hat die innere Funktion Zugriff auf die in der äußeren Funktion deklarierten Variablen.Verschlüsse lassen sich am besten anhand von Beispielen erklären.In Listing 2-18 können Sie feststellen, dass die innere Funktion Zugriff auf eine Variable (VariableInouterfunktion) aus dem äußeren Bereich hat.Die Variablen in der äußeren Funktion wurden durch die innere Funktion geschlossen (oder darin eingebunden).Daher der Begriff Schließung.Das Konzept an sich ist einfach genug und ziemlich intuitiv.

Listing 2-18:
    function outerFunction(arg) {
     var variableInOuterFunction = arg;

     function bar() {
             console.log(variableInOuterFunction); // Access a variable from the outer scope
     }
     // Call the local function to demonstrate that it has access to arg
     bar(); 
    }
    outerFunction('hello closure!'); // logs hello closure!

Quelle: http://index-of.es/Varios/Basarat%20Ali%20Syed%20(auth.)-Beginning%20Node.js-Apress%20(2014).pdf

Bitte werfen Sie einen Blick auf den folgenden Code, um den Abschluss besser zu verstehen:

        for(var i=0; i< 5; i++){            
            setTimeout(function(){
                console.log(i);
            }, 1000);                        
        }

Was wird hier ausgegeben? 0,1,2,3,4 das wird nicht sein 5,5,5,5,5 wegen Schließung

Wie wird es also gelöst?Die Antwort ist unten:

       for(var i=0; i< 5; i++){
           (function(j){     //using IIFE           
                setTimeout(function(){
                               console.log(j);
                           },1000);
            })(i);          
        }

Lassen Sie es mich einfach erklären: Wenn eine Funktion erstellt wurde, passierte nichts, bis sie die for-Schleife im ersten Code aufrief, die fünfmal aufgerufen wurde, aber nicht sofort aufgerufen wurde, also wenn sie aufgerufen wurde, d in var i und schließlich ausführen setTimeout Funktion fünf Mal und drucken 5,5,5,5,5

Hier erfahren Sie, wie es mithilfe von IIFE gelöst wird, d. h. „Immediate Invoking Function Expression“.

       (function(j){  //i is passed here           
            setTimeout(function(){
                           console.log(j);
                       },1000);
        })(i);  //look here it called immediate that is store i=0 for 1st loop, i=1 for 2nd loop, and so on and print 0,1,2,3,4

Für weitere Informationen lesen Sie bitte den Ausführungskontext, um den Abschluss zu verstehen.

  • Es gibt eine weitere Lösung, um dieses Problem mithilfe von let (ES6-Funktion) zu lösen, aber unter der Haube funktioniert die obige Funktion

     for(let i=0; i< 5; i++){           
         setTimeout(function(){
                        console.log(i);
                    },1000);                        
     }
    
    Output: 0,1,2,3,4
    

=> Weitere Erklärung:

Wenn Sie im Speicher eine for-Schleife ausführen, erstellen Sie ein Bild wie folgt:

Schleife 1)

     setTimeout(function(){
                    console.log(i);
                },1000);  

Schleife 2)

     setTimeout(function(){
                    console.log(i);
                },1000); 

Schleife 3)

     setTimeout(function(){
                    console.log(i);
                },1000); 

Schleife 4)

     setTimeout(function(){
                    console.log(i);
                },1000); 

Schleife 5)

     setTimeout(function(){
                    console.log(i);
                },1000);  

Hier wird i nicht ausgeführt und nach der vollständigen Schleife habe ich den Wert 5 im Speicher gespeichert, aber sein Gültigkeitsbereich ist immer in der untergeordneten Funktion sichtbar, wenn also die Funktion darin ausgeführt wird setTimeout fünf Mal aus, wenn es gedruckt wird 5,5,5,5,5

Um dieses Problem zu beheben, verwenden Sie IIFE wie oben erläutert.

Curry:Damit können Sie eine Funktion teilweise auswerten, indem Sie nur eine Teilmenge ihrer Argumente übergeben.Bedenken Sie:

function multiply (x, y) {
  return x * y;
}

const double = multiply.bind(null, 2);

const eight = double(4);

eight == 8;

Schließung:Ein Abschluss ist nichts anderes als der Zugriff auf eine Variable außerhalb des Gültigkeitsbereichs einer Funktion.Es ist wichtig, sich daran zu erinnern, dass eine Funktion innerhalb einer Funktion oder einer verschachtelten Funktion kein Abschluss ist.Abschlüsse werden immer dann verwendet, wenn auf Variablen außerhalb des Funktionsbereichs zugegriffen werden muss.

function apple(x){
   function google(y,z) {
    console.log(x*y);
   }
   google(7,2);
}

apple(3);

// the answer here will be 21

Der Verschluss ist sehr einfach.Wir können es wie folgt betrachten:Abschluss = Funktion + seine lexikalische Umgebung

Betrachten Sie die folgende Funktion:

function init() {
    var name = “Mozilla”;
}

Wie sieht die Schließung im oben genannten Fall aus?Funktion init() und Variablen in ihrer lexikalischen Umgebung, z. B. Name.Schließung = init() + Name

Betrachten Sie eine andere Funktion:

function init() {
    var name = “Mozilla”;
    function displayName(){
        alert(name);
}
displayName();
}

Welche Schließungen gibt es hier?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, sofern vorhanden.

Abschluss 1: init-Funktion + (Namensvariable + displayName()-Funktion) -> lexikalischer Geltungsbereich

Abschluss 2: displayName-Funktion + (Namensvariable) -> lexikalischer Geltungsbereich

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top