2010-09-08 54 views
5

運行下面的代碼:JavaScript的作用域與封閉:幫助我理解

for (var i=0; i<3; i++) { 
    setTimeout(function() { console.log(i); } , 500); 
} 

輸出 「3」 三次。當創建內部函數時,它將輸出i的最終值,而不是i的值。

如果我想要輸出爲1,2和3,我會如何編寫此代碼?在函數定義的時候,我怎樣才能使用i的值,而不是最終值呢?

回答

6
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其餘代碼使用。

+0

此示例使用兩個匿名函數,而@ z5h的答案使用名爲功能,這可以更清楚地說明概念。 – palswim 2010-09-08 21:58:18

4
function f(i){ 
    return function(){console.log(i);}; 
} 

for (var i=0; i<3; i++) { 
    setTimeout( 
    f(i) 
    , 500); 
} 
1

替代:

for (var i=0; i<3; i++) { 
    (function(val){ 
     setTimeout(function() { 
      console.log(val); 
     },500) 
    }(i)); 
} 
2

現代的替代明確封閉(可以得到一點毛茸茸的,當你有一個雙層包裝函數讀取)是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); 
}