'setTimeOut' ruft in JavaScript 'für' Schleifen auf, warum scheitern sie? [Duplikat]

8

Lassen Sie mich meine Frage klären. Ich frage nicht, wie man den folgenden Code funktioniert. Mir ist bewusst, dass Sie das Schlüsselwort let oder ein iffe verwenden können, das seinen eigenen Wert von i erfasst. Ich brauche nur Klarheit darüber, wie auf den Wert i im folgenden Code zugegriffen wird. Ich lese den folgenden Blogpost darüber, wie es ist, dass der folgende Code nicht funktioniert. Blogpost

%Vor%

Der Verfasser behauptet, dass der Code nicht funktioniert, weil wir die Variable i als Referenz anstelle eines Wertes übergeben. Das heißt, anstatt den Wert von i pro Iteration anzugeben, stellen wir die Variable dem Callback in setTimeout als Referenz zur Verfügung. In der Tat, wenn die Schleife beendet und Rückrufe ausgelöst werden, haben wir einen Verweis auf die Variable i , die 6 sein wird. So funktioniert es?

Hier ist mein Verständnis. Mein Verständnis ist, dass wir nichts an die Callbacks der Funktion setTimeout weitergeben, wenn die Schleife ausgeführt wird. Wir richten lediglich die asynchronen Aufrufe ein. Wenn die Closure-Callback-Funktionen ausgeführt werden, suchen sie auf der Grundlage der lexikalischen Scoping-Regeln nach der Variablen i . Das heißt, die Closures sehen in dem Bereich aus, in dem die Callbacks geschlossen haben, was wiederum in diesem Fall 6 wäre, da es abgeschlossen ist, nachdem die for -Schleife abgeschlossen ist.

Welches ist es, löst die Funktion den Wert von i auf 6 basierend auf der Variablen, die als Referenz bei jeder Iteration übergeben wird, oder aufgrund von lexikalischem Scoping?

    
Hugo Perea 31.12.2017, 06:45
quelle

2 Antworten

15

Sie haben Recht, dass das lexikalische Scoping die Ursache für dieses Verhalten ist. Wenn die Timer-Funktionen ausgeführt werden, versuchen sie, i aufzulösen, und sie müssen den Scope-Kette um es zu finden. Aufgrund von lexikalischem Scoping ist i nur einmal in der Scope-Kette vorhanden (ein Bereich höher als die Timer-Funktionen), und zu diesem Zeitpunkt ist i 6 , weil die Schleife an diesem Punkt beendet wurde.

Das Schlüsselwort var bewirkt, dass Variablen in JavaScript entweder einen Funktions- oder einen globalen Bereich haben (abhängig davon, wo diese Deklaration ist). In Ihrem Code bewirkt var i , dass die Variable i global existiert (weil sich Ihr Code nicht in einer Funktion befindet) und jede Timer-Funktion dieselbe einzelne i auflösen muss, wenn sie schließlich ausgeführt wird. Da die Timer-Funktionen nicht ausgeführt werden, bis die Schleife beendet ist, hat i den letzten Wert (6), den die Schleife verursacht hat.

Ändern Sie var i in let i , um einen Blockbereich zu erstellen für i , um das Problem zu lösen.

let erstellt einen Blockbereich für die Variable. Bei jeder Iteration der Schleife geben Sie den Schleifenblock erneut ein, und für i wird ein separater Bereich erstellt, den jede Zeitgeberfunktion zu sich selbst führt.

%Vor%
    
Scott Marcus 31.12.2017, 06:50
quelle
5

Lassen Sie mich mit Ihrem Code erklären:

%Vor%

Im Moment, als die Funktion setTimeout() ausgelöst wurde, wird die Variable von i gleich 1,2,3,4,5 sein wie erwartet, bis der Wert von i auf 6 und steigt Stoppen Sie das For-Looping.

%Vor%

Nach einer gewissen Zeit wird der Rückruf von timeout ausgelöst und loggt log Wert von i . Schau mal oben, wie gesagt, der Wert von i war schon 6.

%Vor%

Die Ursache ist das Fehlen von ECMAScript 5: block scope . (var i = 1;i <=5 ;i++) erstellt eine Variable, die in der gesamten Funktion vorhanden ist, und kann von der Funktion im lokalen Bereich oder im Abschlussbereich geändert werden. Aus diesem Grund haben wir in ECMAScript 6 let .

Es kann leicht behoben werden, indem var in let geändert wird:

%Vor%     
Kai 31.12.2017 07:00
quelle

Tags und Links