2011-11-30 102 views
3

我知道這段代碼不起作用,我也知道爲什麼。 不過,我不知道如何解決它:在包含循環變量的循環中定義匿名函數?

的JavaScript:

var $ = function(id) { return document.getElementById(id); }; 
document.addEventListener('DOMContentLoaded', function() 
{ 
    for(var i = 1; i <= 3; i++) 
    { 
     $('a' + i).addEventListener('click', function() 
     { 
      console.log(i); 
     }); 
    } 
}); 

HTML:

<a href="#" id="a1">1</a> 
<a href="#" id="a2">2</a> 
<a href="#" id="a3">3</a> 

我希望它打印您單擊的鏈接的數量,而不是僅僅「 4" 。 我寧願避免使用節點(id或內容)的屬性,而是修復循環。

+0

[Coffeescript](http://coffeescript.org/)爲這種情況提供了非常方便的'do'關鍵字。 –

回答

5

裹在自己的匿名函數環形塊:

document.addEventListener('DOMContentLoaded', function() 
{ 
     for(var i = 1; i <= 3; i++) 
     { 
      (function(i) { 
       $('a' + i).addEventListener('click', function() { 
        console.log(i); 
       }) 
      })(i); 
     } 
} 

這將創建的i一個新實例局部於在每次調用/迭代內部函數。如果沒有這個本地副本,每個函數傳遞給addEventListener(在每次迭代中)通過對相同變量的引用關閉,其值等於4任何這些回調執行的時間。

3

問題是內部函數在i上創建了一個閉包。這意味着,基本上,該功能不僅僅是在設置處理程序時記住i的值,而是記憶變量i本身;它保留對i的實時參考。

你必須休息關閉通過傳遞i的功能,因爲這將導致複製的i作出。

一個常見的方法是使用匿名函數立即執行。

 for(var i = 1; i <= 3; i++) 
     { 
      $('a' + i).addEventListener('click', (function(localI) 
      { 
       return function() { console.log(localI); }; 
      })(i); 
     } 

既然你已經使用jQuery,我會提到jQuery提供一個data功能,可用於簡化這樣的代碼:

 for(var i = 1; i <= 3; i++) 
     { 
      $('a' + i).data("i", i).click(function() 
      { 
       console.log($(this).data("i")); 
      }); 
     } 

在這裏,而不是通過打破封閉將i傳遞給一個匿名函數,您可以通過將i傳遞給jQuery的data函數來打破它。

+0

+1用於描述關閉問題。 – link664

+0

我沒有使用jQuery只是基本的$ = document.getElementById;) – Tyilo

+0

我想知道你爲什麼要做'$('a'+ i).addEventListener'而不是'$('a'+ i).click ' - 哦,一些多餘的jQuery信息:) –

0

閉包捕獲變量的引用,而不是副本,這就是爲什麼它們都會導致'i'的最後一個值。

如果你想捕獲一份副本,那麼你需要將它包裝在另一個函數中。