2012-02-22 26 views
1

我是prob。在這裏很密集,但我無法弄清楚下面的代碼到底發生了什麼。在for循環中創建閉包 - 我正在做這個對嗎?

我想要做的是將兩個單獨的處理程序附加到字段的更改事件。每個處理程序都是通過遍歷一個數組來設置的,並使用數組中的項來在處理程序運行時生成處理程序的輸出 - 希望在查看示例代碼時變得清晰。

代碼如下:

$(document).ready(function() { 
     // 
     // Create some test input fields on the page... 
     // 
     $('<br />').insertAfter($('body > *:last')); 
     $('<input type="text" name="t0" id="t0" value="" />').insertAfter($('body > *:last')); 
     $('<input type="text" name="t1" id="t1" value="" />').insertAfter($('body > *:last')); 

     // 
     // The problematic part - for me at least... 
     // 
     var arr = new Array(1, 2); 
     for (var a in arr) { 
      // Using Chrome console here for logging 
      console.log("## " + a); 
      $('#t0').change(function() { 
       console.log(">> " + a) 
      }); 
     } 
    }); 

那麼,我希望當我的值添加到第一場出現的情況是,從控制檯(在Chrome瀏覽器上運行這些例子)中:

## 0 
## 1 
>> 1 
>> 2 

我得到的是:

## 0 
## 1 
>> 1 
>> 1 

我本來期望傳遞給處理函數會在a的值上形成一個閉包,並且我將結束兩個函數綁定到處理程序,其中a的值爲1,其中a的值爲2

想法?

乾杯 - 克里斯

+0

快速提示:您不關閉文檔就緒功能。添加一個右括號')' – musefan 2012-02-22 17:53:38

+0

瞭解JavaScript函數範圍將幫助您:http://stackoverflow.com/questions/7774636/jquery-event-handler-created-in-loop – paislee 2012-02-22 17:58:12

+0

添加缺少')'@musefan - 謝謝 – Kris 2012-02-22 18:10:21

回答

4

這裏有兩個大錯誤:

首先,像你期望它的for (a in x)不起作用:它遍歷對象的屬性,而不是在數組元素。

另一個錯誤是a在函數被調用的時候發生了變化。以實現所需的功能的一個好方法是這樣的:

for(var a=0; a<arr.length; a++) { 
    (function(a) { 
     // now you can use "a" 
    })(arr[a]); 
} 

要查看與for循環會發生什麼,如果你不創建一個閉包,看到這一點:

var arr = [1,2,3]; 
var functions = []; 

for(var a=0; a<arr.length; a++) { 
    functions.push(function() { 
     console.log(a); 
    }) 
} 

// now execute all the functions 
for(var i=0; i<functions.length; i++) { 
    functions[i](); 
} 

現在所有的功能將記錄3,這是數組中最後一個元素的索引+ 1(arr[0] == 1, arr[1] == 2, arr[2] == 3)。會發生什麼情況是for循環會在每次迭代中創建這些函數,但在循環結束時會執行,此時a == arr.length

+0

感謝您的快速響應!對我所犯的錯誤有很好的解釋。現在有道理 - 非常感謝。 – Kris 2012-02-22 18:06:17

2

而不是「for」循環,試試jQuery的own tool

$(arr).each(function(a) { 
     console.log("## " + a); 
     $('#t0').change(function() { 
      console.log(">> " + a) 
     }); 
    }); 

這按預期工作。

您的代碼無法工作的原因是閉包使用其變量的最新值。也就是說,如果創建一個等於1的關閉a,然後在a等於2時創建另一個關閉,則兩個關閉都將使用最後一個值2。這是無可否認的,但這是它的工作原理。

+0

感謝您的迴應 - 也有助於我的理解,所以謝謝! – Kris 2012-02-22 18:07:48

-1

你必須做這樣的事情:

for (var a in arr) { 
    (function (a) { 
     // thanks to closure variable a is local here 
     $('#t0').change(function() { 
      console.log(">> " + a); 
     }); 
    }(a)); 
} 

或像這樣:

for (var a in arr) { 
    $('#t0').change((function (a) { 
     return function() { 
      // returned function has access to local variable a from 
      // outer function 
      console.log(">> " + a); 
     }; 
    }(a)); 
} 

訣竅是把變量比for循環更深的範圍。 更容易閱讀上述兩個片段可以寫成:

for (var a in arr) { 
    (function (inner_a) { 
     // thanks to closure variable a is local here 
     $('#t0').change(function() { 
      console.log(">> " + inner_a); 
     }); 
    }(a)); 
} 

for (var a in arr) { 
    $('#t0').change((function (inner_a) { 
     return function() { 
      // returned function has access to local variable a from 
      // outer function 
      console.log(">> " + inner_a); 
     }; 
    }(a)); 
} 

BTW。除非您通過對象屬性進行交互,否則最好使用經典(var i ...),而不是for .. in循環。並且應該緩存$('#t0'):

var cachedEl = $('#t0'); 

for (var a=0; a < arr.length; a++) { 
    (function (inner_a) { 
     // thanks to closure variable a is local here 
     cachedEl.change(function() { 
      console.log(">> " + inner_a); 
     }); 
    }(a)); 
} 
// or 
for (var a=0; a < arr.length; a++) { 
    cachedEl.change((function (inner_a) { 
     return function() { 
      // returned function has access to local variable a from 
      // outer function 
      console.log(">> " + inner_a); 
     }; 
    }(a)); 
} 
+0

關於緩存的有趣點。感謝您的答覆。 – Kris 2012-02-28 17:51:19