2016-04-30 14 views
-1

Ich habe versucht, meinen Kopf um Umfang, speziell Verschlüsse einzuwickeln. Ich weiß, dass es viele Beiträge zum Thema gibt, und ich habe viel gelesen. Aber die meisten Orte verweisen auf das Thema als fortgeschritten und verwenden eine relativ schwer zu erfassende Terminologie. Ich würde gerne absolut sicher sein, dass ich die Grundlagen richtig verstanden habe, damit ich mich nicht in die komplizierteren Themen mit einer falschen Vorstellung davon begeben kann, wie Funktionen wirklich funktionieren.Verschachtelte Funktionen, Verschlüsse und Bereich

Also ... Ich wählte eine grundlegende Funktion, und würde wirklich gerne für jemanden, mir zu sagen, was ich denke, es passiert unter der Haube ist, was tatsächlich passiert.

Dies ist der Code:

function sum(a) { 


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

console.log(sum(1)(sum(2))) 

(. Ich weiß, dass es die Summe nicht wirklich ist zu tun, ich mit ihm gezwickt wurde, um zu versuchen, zu verstehen, was in jedem Schritt vorging)

Also, mein Hauptbezweifelt war, warum A 1 war, und nicht 2. Ich kam zu dem Schluss, dass die Schließung erstellt wird, sobald function(b) erstellt wird, um als ein Argument zu nehmen, direkt nach der Rückkehr von sum(1). Daher nehme ich bei der Definition von Closure an, dass zum Zeitpunkt der Erstellung der Funktion auch die lexikalische Umgebung gespeichert wird (in der a = 1). Ist das richtig?

Ich habe ein Diagramm der Schritte gemacht.

Diagram

+0

ich den Code zu verstehen, aber nicht das Diagramm, so dass nicht klar ist, was man von der Hauptstadt bedeutet 'A's I bin mir nicht sicher, wie ich antworten soll. Beachten Sie, dass 'sum (2)' eine neue Funktion zurückgibt, die Sie nie aufrufen, daher ist ihre Schließung hier irrelevant. Da "sum (2)" eine neue Funktion liefert, warum würden Sie das als Argument für die Rückkehr von "sum (1)" verwenden? Es macht keinen Sinn, die Nummer 1 zu einer Funktionsreferenz hinzuzufügen. JS wird es tun (was zu einer Zeichenkette führt), aber es scheint ein ziemlich seltsamer Testfall zu sein. – nnnnnn

+0

@nnnnnn Es soll 'a = 1' sein, und jedes Rechteck stellt eine lexikalische Umgebung dar. Ok, ich denke, was du gerade gesagt hast über die Rückkehr von 'sum (2)' nie genannt zu werden, hilft, einen Sinn daraus zu machen. Nur um zu verdeutlichen, gibt es noch zwei Schließungen hier richtig? Eins, wenn 'sum (1)' zuerst definiert wird und das andere für 'sum (2)' –

+0

@Sean Sie haben mehrere Antworten erhalten, aber keine akzeptiert. Was genau verursacht dir schon Probleme? – rand

Antwort

-1

Was auch immer Anlage verwenden wir eine innere Funktion außerhalb ihres lexikalischen Gültigkeitsbereich zu transportieren, wird es einen Umfang Bezug zu halten, wo sie ursprünglich erklärt wurde, und wo immer wir sie ausführen, wird die Schließung ausgeübt werden .

In der nächsten Funktion sehen Sie, wie die greet eine Variable salute sogar innerhalb der greeting Funktion deklariert verwenden, wenn diese nicht mehr genannt wird dies eine Schließung genannt.

function greeting(name) { 
    var salute = "Hello "; // This is a closure 

    return function() { 
    console.log(salute + name); 
    } 
} 

var greet = greeting("Dave"); 

greet(); // Hello Dave 

Sie können viel mehr über Schließungen in der Buchreihe You Don't Know JS aber für dieses spezielle Thema Check You Don't Know JS: Scope & Closures Kyle Simpson lernen. Er verwendet eine einfache und auf den Punkt gebrachte Sprache, um schwierige Konzepte wie diesen zu erklären.

+1

Wie funktioniert das Wiederholen von Code ähnlich, aber nicht ganz derselbe wie in der Frage? Der OP-Code gibt auch eine Funktion zurück, die auf eine Variable (ein Argument) der äußeren Funktion zugreift. – nnnnnn

+0

Ist nicht das gleiche und das beweist, dass Sie nicht verstehen, wie Closures oder JavaScript funktioniert, aber wenn Sie die Links lesen, die ich geteilt habe, werden Sie. –

+1

Ich sagte "ähnlich, aber nicht ganz gleich". Sie haben ein generisches Closures-Beispiel angegeben, das sich nicht signifikant von der Funktion 'sum()' in der Frage unterscheidet, und Sie haben die gestellte Frage nicht beantwortet. – nnnnnn

-1

hier ist das, was

1) passiert Wenn eine Funktion eine Funktion zurückgibt und zurück Funktion sofort aufgerufen wird, dass currying (A funktionale Programmierung Begriff) genannt wird. Sie haben in diesem Beispiel beide Konzepte currying und closure gemischt.

2) Zuerst wird Ihr sum(1) Teil aufgerufen. welches function(b) {return a+b} zurückgibt (lässt uns es als # 1 bezeichnen), aber mit Es wird a als 1 für den Kontext von # 1 nur am Leben erhalten.

3) Da das Funktionsargument ein Funktionsaufruf selbst ist, wird dieser Argumentteil aufgerufen. z. B. sum(1)(sum(2)), hier sum(2) Teil wird aufgerufen und es gibt function(b) {return a+b} zurück (lässt es als # 2 bezeichnen), auch Es wird am Leben a als 2 für den Kontext von # 2nd nur (Schließung).

4) Jetzt sind wir sofort # 1. mit # 2. als Parameter mit dieser currying Syntax aufrufen - # 1. (# 2)

5) so unsere a ist nicht zugewiesenen variabel und hat Wert 1, b Variable hat eine Wert function(b) {return a+b}. Wie wir diese beiden verketten, ist die endgültige Ausgabe 1function(b) {return a+b}

N.B. - a) Wenn Sie eine Summe von a + b und nicht diese seltsame Ausgabe wollten, ändern Sie einfach Ihre letzte Zeile als console.log(sum(1)(2)). b) Falls Sie die Schließung a mit einem Wert von 2 in der Funktion bemerkt haben, die als # 2 bezeichnet wird, wird nie irgendwo benutzt, sondern lebendig.

+1

Ihre Definition von "currying" ist falsch: Ob die zurückgegebene Funktion sofort aufgerufen wird, hat nichts mit Currying zu tun, noch sind alle Funktionen, die eine Funktion zurückgeben, Beispiele für Currying. Warum würde es auch einen Speicherverlust geben, wenn keine Referenzen auf die zurückgegebenen Funktionen beibehalten werden? – nnnnnn

+0

1) Ya wurde zu "potentiellem Leck, wenn gespeichert" 2) Ich definiere nicht currying, ich erklärte Currying in einfachen Worten. – sapy

+0

@sapy Vielen Dank, dass Sie sich die Zeit genommen haben. Aber ich folge deiner Erklärung nicht wirklich. Was meinst du damit, wenn du sagst: "a is closure"? Ist die Schließung keine Funktion und ihre lexikalische Umgebung? Eine Sache noch. Sie sagen, dass Summe (2) zuerst aufgerufen wird, aber diese Website verwendet: http://pythontorer.com/javascript.html#code=function+sum(a%29+%7B%0A%0A%0A++return+function (b% 29 +% 7B ++% 0A ++++ Rendite + a% 2Bb% 0A ++% 7D% 0A% 7D% 0A% 0Aconsole.log (+ Summe (1% 29 (Summe (2% 29% 29% 29 & mode = display & origin = opt-frontend.js & kumulativ = false & heapPrimitives = falsch & textReferenzen = false & py = js & rawInputLstJSON =% 5B% 5D & curInstr = 0 –

0

Wenn sum aufgerufen wird, wird ein Bereich erstellt. In diesem Bereich gibt es den formalen Parameter a und eine innere, anonyme Funktion, die sofort zurückgegeben wird. Diese anonyme Funktion ist eine Closure, weil sie den Umfang ihrer umschließenden Funktion (sum) erfasst. Folglich hat diese innere Funktion Zugriff auf a.

Nun kommen wir zu dem Punkt, der Sie scheinbar verwirrt: Die innere Funktion erhält nur eine Kopie des Umfangs der Summe, nicht eine Referenz auf die ursprüngliche. Das heißt, wenn wir von sum zurückkehren und damit seinen Geltungsbereich aufheben, bleibt diese Kopie unberührt (a der inneren Funktion bleibt 1). Weitere Funktionsaufrufe von sum mit unterschiedlichen Argumenten wirken sich auch nicht auf die Schließung aus.

Fazit: Ein Verschluss kann länger bestehen als seine umschließende Funktion.

Technisch a von sum gesprochen wird in dem Stapel gespeichert ist, während die aufgenommenen a des Verschlusses in dem Heap gespeichert ist, und ist somit unabhängig von der Lebensdauer der sum.

Übrigens, was Sie hier tun, heißt currying. Statt sum mit mehreren Argumenten fordern Sie es prozedural nennen, mit einem einzigen Argumente pro Aufruf:

sum(1, 2); // multi argument form 
sum(1)(2); // curry form