2011-11-30 81 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'的最后一个值。

如果你想捕获一份副本,那么你需要将它包装在另一个函数中。