2014-12-06 46 views
0

我以爲我用Javascript理解了閉包,但顯然我沒有。以下代碼不起作用。如何在延遲1秒後打印出控制檯中從0到9的所有數字?目前它只打印「未定義」十次。帶有Javascript回調的閉包

注意:我不想尋找一個簡單的鍛鍊來延遲打印數字。這個問題是關於理解閉包。

<script> 
for(var i=0;i<10;i++){ 
    setTimeout(function(i){console.log(i)}, 1000) 
} 
</script> 

回答

2

閉包是在JavaScript中很重要,但要了解你是closing正是在這一點很重要。重做你當前的代碼(種類)。

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

在這個例子中,你的代碼基本上是關閉了var i;,這意味着當定時器運行時,它會讀取該的var i;值並打印出來。在這種情況下,如您所見,定時器運行時,循環已完成,值爲10

你想要做的是創建一個新的函數範圍,在特定的時間捕獲i的值。

function(){ 
    var i; 
    for(i=0;i<10;i++){ 
     (function(iInner){ 
      setTimeout(function(){ 
       console.log(iInner); 
      }, 1000); 
     })(i); 
    } 
} 

這個例子將創建一個新的匿名函數,然後在循環立即調用它,並傳遞的i當前值到它,所以,當你的計時器讀取iInner,它會讀取這可是價值傳遞給函數,而不是來自var i;的值。如果需要,您也可以撥打iInneri,但爲了清晰起見,我使用了兩個不同的名稱。

還有一些助手可以使用,例如.bind,它們本質上會自動爲您創建一個新的匿名函數,並傳遞像這樣的參數。

function(){ 
    var i; 
    for(i=0;i<10;i++){ 
     setTimeout(function(iInner){ 
      console.log(iInner); 
     }.bind(null, i), 1000); 
    } 
} 

<func>.bind將採取i的值,並返回調用時通過對<func>那些ARGS一個新的功能,您就不必創建嵌套的另一層。

0

您已經定義了回調函數取參數i,這是掩蓋在聲明for循環closurable i。因此,將其更改爲:

<script> 
for(var i=0;i<10;i++){ 
    setTimeout(function(){console.log(i)}, 1000) 
} 
</script> 

編輯:對不起,我不看你的問題的一般意圖。就封閉方式而言,函數對象捕獲對函數體內引用的封閉範圍中的任何變量的引用,該引用不綁定到主體內的任何局部變量(參數或顯式var聲明) 。這就是爲什麼你的原始嘗試不起作用; i綁定到本地參數。

功能對象不會捕獲該合攏變量具有在被定義的功能的時間;它捕獲了一個參考它。因此,您無法迭代單個變量(我正在討論在for循環中聲明的var i),意圖是爲每個後續函數定義關閉。價值觀沒有關閉;變量是。

但是,您可以通過關閉一個臨時本地來有效地關閉一個值,該臨時本地具有您要在定義該函數時捕獲的值。它需要創建一個新的功能範圍和界定範圍內的closuring功能,圍繞本地(函數參數)closuring有你想要的值:

<script> 
for (var i_outer = 0; i_outer < 10; ++i_outer) 
    setTimeout((function(i_inner) { return function() { console.log(i_inner); }; })(i_outer), 1000); 
</script> 
+0

不!這只是打印10次。這就是爲什麼我們需要關閉。 – 2014-12-06 20:46:36

0

這是你怎麼做。您創建了一個IIFE(立即調用的函數表達式),該函數返回一個打印到控制檯的函數。記住我的價值,應該被內在的功能所吞噬,通過將它作爲一個參數來傳遞。

setTimeout需要一個「函數」作爲第一個參數,這就是我們從IIFE返回函數的原因。

for(var i=0;i<10;i++){ 

    setTimeout((function(i) { 
     return function() { 
      console.log(i); 
     } 
    })(i), 1000) 
} 

感謝