運行下面的代碼:JavaScript的作用域與封閉:幫助我理解
for (var i=0; i<3; i++) {
setTimeout(function() { console.log(i); } , 500);
}
輸出 「3」 三次。當創建內部函數時,它將輸出i
的最終值,而不是i
的值。
如果我想要輸出爲1,2和3,我會如何編寫此代碼?在函數定義的時候,我怎樣才能使用i
的值,而不是最終值呢?
運行下面的代碼:JavaScript的作用域與封閉:幫助我理解
for (var i=0; i<3; i++) {
setTimeout(function() { console.log(i); } , 500);
}
輸出 「3」 三次。當創建內部函數時,它將輸出i
的最終值,而不是i
的值。
如果我想要輸出爲1,2和3,我會如何編寫此代碼?在函數定義的時候,我怎樣才能使用i
的值,而不是最終值呢?
for (var i=0; i<3; i++) {
setTimeout(function(val) { return function() { console.log(val); } }(i), 500);
}
所以,在setTimeout
時間(當時我們定義的功能setTimeout
),我們稱之爲匿名函數取val
作爲參數。這將爲每個函數調用創建一個閉包,將val
的值存儲在我們剛剛調用的函數的範圍內。我使用了self-invoking function,它立即創建closure。
在你提供的代碼中,代碼創建了一個閉包,但是對於整個代碼的更大範圍,所以i
是整個代碼的本地代碼,這意味着在運行時,匿名函數將使用變量i
其餘代碼使用。
function f(i){
return function(){console.log(i);};
}
for (var i=0; i<3; i++) {
setTimeout(
f(i)
, 500);
}
替代:
for (var i=0; i<3; i++) {
(function(val){
setTimeout(function() {
console.log(val);
},500)
}(i));
}
現代的替代明確封閉(可以得到一點毛茸茸的,當你有一個雙層包裝函數讀取)是Function#bind
。一旦你的瀏覽器不這樣做的ECMAScript第五版但已經hacked in support,你可以說:
for (var i=0; i<3; i++) {
setTimeout(function(i) { console.log(i); }.bind(window, i), 500);
}
的window
是值this
將裏面的功能(你不需要this
這裏,所以我們只使用默認的全局對象)。在你只是調用另一個函數/方法一樣,這裏console.log
的情況下,你可以用它來切除函數表達式完全:
for (var i=0; i<3; i++) {
setTimeout(console.log.bind(console, i), 500);
}
此示例使用兩個匿名函數,而@ z5h的答案使用名爲功能,這可以更清楚地說明概念。 – palswim 2010-09-08 21:58:18