2012-04-10 8 views
14

Gibt es eine definitive Quelle für die Variablenerfassung in Javascript neben dem Standard (es ist ein Schmerz, den Standard zu lesen)?Verstehen der variablen Erfassung durch Schließungen in Javascript/Knoten

Im folgenden Code wird i von Wert kopiert:

for (var i = 0; i < 10; i++) 
{ 
    (function (i) 
    { 
     process.nextTick(function() 
     { 
      console.log(i) 
     }) 
    }) (i) 
} 

druckt So 1..10. process.nextTick ist ein Analogon von setTimeout(f,0) in Knoten. Er druckt 9 10 mal

for (var i = 0; i < 10; i++) 
{ 
     var j = i 
     process.nextTick(function() 
     { 
      console.log(j) 
     }) 
} 

:

Aber im nächsten Code i scheint nicht kopiert werden. Warum? Ich interessiere mich mehr für einen Referenz-/allgemeinen Artikel als für die Erläuterung dieses konkreten Falles der Erfassung.

Antwort

6

Ich habe keine handliche Referenz. Aber die Quintessenz ist: In der ersten übergeben Sie explizit i an eine anonyme Funktion, die einen neuen Bereich erstellt. Sie erstellen keinen neuen Bereich für i oder j in der Sekunde. Außerdem erfasst JavaScript immer Variablen und keine Werte. So könntest du auch ich ändern.

Das JavaScript var Schlüsselwort hat Funktionsumfang, nicht Blockbereich. Daher erstellt eine for-Schleife keinen Bereich.

Als eine Anmerkung hat das Nicht-Standard let Schlüsselwort lokalen Geltungsbereich.

+0

Es ist nicht klar, warum ich nicht einen neuen Bereich für j – nponeccop

+0

@nponeccop erstellen, ist Javascript Funktionsumfang. –

+0

Ich schlage meinen Kopf gegen den Tisch. Wusste das nicht, angenommen, es ist C++ oder Perl oder Haskell :) Faszinierend – nponeccop

4

Es wird kopiert (oder zugewiesen) in Ihrem zweiten Beispiel, es ist nur, dass es nur eine Kopie der Variablen j gibt und es wird den Wert haben, den es zuletzt hatte, der 9 sein wird (die letzte rev Ihrer for-Schleife). Sie benötigen einen neuen Funktionsabschluss, um eine neue Kopie einer Variablen für jede Umdrehung der Schleife for zu erstellen. Ihr zweites Beispiel hat nur eine Variable, die allen revs Ihrer for-Schleife gemeinsam ist, daher kann sie nur einen Wert haben.

Ich kenne keine definitive Beschreibung zu diesem Thema.

Variablen in Javascript sind auf die Funktionsebene beschränkt. In JavaScript gibt es kein Block-Scoping. Wenn Sie also für jede rev der for-Schleife eine neue Version einer Variablen wünschen, müssen Sie eine neue Funktion verwenden (indem Sie eine Funktion schließen), um diesen neuen Wert jedes Mal durch die for-Schleife zu erfassen. Ohne die Funktion closure hat die eine Variable nur einen Wert, der allen Benutzern dieser Variablen gemeinsam ist.

Wenn Sie deklarieren eine Variable wie Ihre var j = i; an einem gewissen anderen Ort als dem Anfang der Funktion, JavaScript, um die Definition an die Spitze der Funktion hisst und Ihr Code wird dazu äquivalent:

var j; 
for (var i = 0; i < 10; i++) 
{ 
     j = i; 
     process.nextTick(function() 
     { 
      console.log(j) 
     }) 
} 

Diese heißt variable hoisting und ist ein Begriff, den Sie googeln können, wenn Sie mehr darüber lesen möchten. Aber der Punkt ist, dass es nur einen Funktionsumfang gibt, so dass eine Variable, die irgendwo in einer Funktion deklariert ist, einmal am Anfang der Funktion deklariert wird und dann an einer beliebigen Stelle in der Funktion zugewiesen wird.

+0

Aktualisierte meine Antwort mit mehr Details zu Ihrer Situation. – jfriend00

4

In JavaScript, umschließen Variablen, die in einem Bereich außerhalb ihrer eigenen definiert wurden, so dass sie einen "lebenden" Verweis auf die Variable haben, keine Momentaufnahme ihres Wertes zu einem bestimmten Zeitpunkt.

Also in Ihrem zweiten Beispiel, erstellen Sie zehn anonyme Funktionen (in process.nextTick(function(){...})), die die Variable j (und i, die immer den gleichen Wert haben, wenn die anonyme Funktion erstellt wird) einschließen. Jede dieser Funktionen verwendet den Wert j zu einer Zeit nach die äußere for-Schleife hat vollständig ausgeführt, so j=i=10 zum Zeitpunkt, dass jede der Funktionen aufgerufen wird. Das heißt, zuerst wird Ihre for-Schleife vollständig ausgeführt, dann werden Ihre anonymen Funktionen ausgeführt und verwenden den Wert j, der bereits auf 10 gesetzt ist!

In Ihrem ersten Beispiel ist die Situation ein wenig anders. Indem Sie den Aufruf an process.nextTick(...) in seiner eigenen anonymen Funktion binden und den Wert i durch Aufrufen der Wrapperfunktion (und Shadowing der alten Variablen i in den Funktionsparameter i) in einen funktionslokalen Bereich binden, erfassen Sie den Wert der Variable i bei Moment, anstatt die beiliegende Referenz zu i, deren Wert in der Anlage der inneren anonymen Funktionen ändert.

Um das erste Beispiel etwas zu verdeutlichen, versuchen Sie, die anonyme Wrapperfunktion zu ändern, um ein Argument mit dem Namen x ((function (x) { process.nextTick(...); })(i)) zu verwenden. Hier sehen wir deutlich, dass x den Wert in i in dem Moment nimmt, in dem die anonyme Funktion aufgerufen wird, so dass es jeden der Werte in der for-Schleife (1..10) bekommt.

+0

Was ist die Best-Practice-Lösung, um den Wert zu erfassen, anstatt die beigefügte Referenz beizubehalten? –

Verwandte Themen