2009-10-16 163 views
2

我遇到了一些JavaScript代碼的問題。JavaScript可變範圍

腳本

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

輸出

5,5,5,5,5,而不是1,2,3,4,5

我種理解爲什麼這不起作用,但我想知道是否有人能向我解釋發生了什麼,以及爲什麼它不起作用!

此外,如何克服這個範圍問題?

回答

5

setTimeout回調函數是異步執行的,所有的console.log電話你讓指同一i變量,並在它們被執行時,for循環已經結束i包含4個。

你可以換一個函數裏你的內心setTimeout呼叫接受一個參數,以存儲正在迭代所有i值的參考,像這樣:

setTimeout(function() { 
    for (var i = 0; i < 5; i++) { 
     (function (j) { // added a closure to store a reference to 'i' values 
     setTimeout(function() { 
      console.log(j); 
     }, j * 200); 
     })(i); // automatically call the function and pass the value 
    } 
}, 200); 

檢查我的回答如下更多細節問題:

+0

感謝您的回覆並回答了其他問題!它完美地解釋了它! – 2009-10-18 13:10:50

+0

@Bisbo:不客氣,很高興幫助! – CMS 2009-10-20 06:42:37

+0

@bdukes:回滾編輯,'i'的最後一個值是'4',注意for循環中的'i <5'條件。 – CMS 2009-10-20 06:44:08

0

因爲您正在訪問設置超時中使用的所有函數中的相同變量i。 setTimeout函數將該函數設置爲在與i變量相同的線程上觸發將來的毫秒數。 i值不會複製到函數中,函數會在引發時引用實際變量i。因爲您已經通過父功能循環,直到i = 5,並且在其他任何事情有機會觸發之前完成,它們全部顯示爲5.

2

看一看this question。它可以幫助你更好地理解範圍和封閉性,非常類似於你的問題。

+0

感謝偉大的鏈接!它完美地解釋它! – 2009-10-18 13:09:52

1

變量i存在於外部函數的範圍內。

它隨循環運行而變化。

內部函數引用它。

嘗試這樣:

var i_print_factory = function (value) { 
    return function() { 
    console.log(value); 
    }; 
}; 

var init_timers = function() { 
    for (var i = 0; i < 5; i++) { 
    setTimeout(i_print_factory(i), i * 200); 
    } 
}; 

setTimeout(init_timers, 200); 
2

你想創建一個封閉包含變量「i」。但是閉包只能在函數結尾創建。因此,如果您的函數是在for循環中創建的,則它們都將具有上次迭代的值。

你可以用這樣的修正:

var createFunction = function(index) { 
    return function() { 
    console.log(index); 
    } 
}; 

for (var i = 0; i < 5; i++) { 
    setTimeout(createFunction(i), i * 200); 
} 

,你從另一個函數返回的功能。