2010-10-18 380 views
5

我的問題其實是理解之一 - 我有一個工作解決方案,我只是不明白它是如何工作的。變量全局範圍理解問題

好吧,我正在做的是在循環中添加一個setTimeout,並通過它傳遞一個改變的值。例如:

for (i=0;i<11;i++) 
{ 
    setTimeout("alert(i)",1000); 
} 

如果我理解正確,這並不工作,因爲JavaScript不(例如PHP)通過我的給函數的值,但通過我的參考 - 這又不是靜態的,而是繼續與櫃檯一起改變。

我找到了解決辦法,這是這樣的:

for (i=0;i<11;i++) 
{ 
    setTimeout(function(x){return function(){alert(x)};}(i),1000); 
} 

我真的不明白這是什麼實際上做。它看起來像將「alert」函數傳遞給調用函數,但我無法理解這一點。

我可以使用此解決方案並將其適應於其他上下文,但我真的很想了解我的所有代碼,而不僅僅是使用我在某處找到的東西,並且樂於使用它。此外,我正在尋找更薄的版本來實現相同的目標。

感謝,馬可

+3

+1「我真的很想了解我的所有代碼,不僅僅是使用我在某處找到的東西,而是爲它感到高興」 – 2010-10-18 15:56:39

回答

2

你調用一個返回函數的函數的原因是,你需要有某種方式傳遞給setTimeout()的功能必須的i當前值的參考。

由於代碼等待爲1000毫秒運行時,for循環將是完整的,然後再運行,和值,如果i將是11

但由於功能都有自己的變量範圍,你可以通過值i放入正在被立即調用的函數中,以便它被本地變量x引用,當setTimeout()最終調用它時,返回的函數可以引用它。

for (i=0; i<11; i++) { 
    setTimeout(function(x){ 
       // CONTINUE HERE: 
       // x is a local variable to the function being executed 
       // which references the current value of i 

       // A function is being returned to the setTimeout that 
       // references the local x variable 
       return function(){ alert(x); }; 

       }(i) // START HERE: 
        // The "outer" function is executed immediately, passing the 
        // current value of "i" as the argument. 
    ,1000); 
} 

所以你結束了一個相當的,將是這樣的:

setTimeout(function(){ alert(x); }, 1000); //...where x === 0 
setTimeout(function(){ alert(x); }, 1000); //...where x === 1 
setTimeout(function(){ alert(x); }, 1000); //...where x === 2 
setTimeout(function(){ alert(x); }, 1000); //...where x === 3 
// etc. 
+0

帕特里克,非常感謝。這可能是缺失的環節。 – 2010-10-18 23:57:00

+0

@Marco P. - 不客氣。 :o) – user113716 2010-10-19 00:13:13

4

這裏做的事情:

function(x){return function(){alert(x)};}(i) 

是它需要一個功能:

function(x){ ...code... } 

並執行它立即,傳遞i(從for環)作爲唯一的參數(這就是最後的(i))。這回報另一個功能:

function(){ alert(x); } 

就這麼結果是真實傳遞給setTimeout()作爲函數呼叫時定時器的了,它不引用在循環情況正在發生變化的變量i,這是使用創建新函數時傳入的副本。

+0

感謝尼克抽出時間向我解釋這一點 - 也感謝以及「+1」。實際上,我的所有頁面(airports.palzkill.de) - 除Google之外 - 都是自制的。 – 2010-10-18 23:56:43

0

帕特里克和尼克幫我大不了在瞭解了整個事情,所以我想總結它爲每個人都與我有同樣的問題:

setTimeout(以及一些其他時間延遲的函數,如eventlisteners)似乎將回調存儲爲一個字符串,然後使用某種內部評估此字符串,從而將其解釋爲代碼。

這會導致循環和時間延遲函數的問題,因爲它們對變量的引用指的是該循環的最終結果,或者可能是一個甚至不是全局的變量。

據我所知,與溶液中的功能-IN-A-函數通過給字符串返回作爲函數的結果,這然後包含值,而不是提及的變量(alert("1")alert(i))解決了這個問題。

關於縮短代碼,我簡單的想法來到了一個簡單的解決方案。作爲回調,預計是一個字符串,爲什麼不寫變量的值到這個字符串,然後還給我:

for (i=0;i<11;i++) 
{ 
    setTimeout("alert("+i+")",1000); 
} 

客觀上這可能不是最好的解決辦法,但它需要的最小量代碼和最少量的大腦資源來理解它如何以及爲什麼會與其他解決方案相反,我現在可以使用它。

再次感謝帕特里克,尼克以及那位抽出時間幫助我解決這個問題的答案!

+0

馬克 - 你已經有部分權利。一個'setTimeout'可以爲它的參數接受一個字符串,然後它會嘗試''評估'。如果全局名稱空間中存在方法(如alert),它將會觸發。但'setTimeout'也可以接受一個函數對象作爲參數。這是通過傳遞一個匿名函數(您的示例中的代碼正在返回)或將一個變量引用傳遞給函數來完成的。如果收到一個函數,它將在全局命名空間中被調用,而不管它是否可能來自私人範圍。在js中,你可以像這樣傳遞函數。 – user113716 2010-10-19 00:36:12