Javascript臭名昭着的循环问题?

我有下面的代码片段。

function addLinks () { for (var i=0, link; i<5; i++) { link = document.createElement("a"); link.innerHTML = "Link " + i; link.onclick = function () { alert(i); }; document.body.appendChild(link); } } 

上面的代码用于生成5个链接,并使用警报事件绑定每个链接以显示当前链接ID。 但它不起作用。 当你点击生成的链接时,他们都会说“链接5”。

但下面的代码片段符合我们的预期。

 function addLinks () { for (var i=0, link; i<5; i++) { link = document.createElement("a"); link.innerHTML = "Link " + i; link.onclick = function (num) { return function () { alert(num); }; }(i); document.body.appendChild(link); } } 

以上两段摘录自这里 。 正如笔者的解释,似乎封闭了魔法。

但是它是如何工作的,以及闭合是如何工作的,都超出了我的理解。 为什么第一个不工作,而第二个工作? 任何人都可以给出关于魔法的详细解释吗?

谢谢。

引用我自己对第一个例子的解释:

JavaScript的作用域是函数级而不是块级,创build闭包只是意味着封闭作用域被添加到封闭函数的词法环境中。

循环结束后,函数级variablesi的值为5,这就是内部函数“看到”的内容。

在第二个示例中,对于每个迭代步骤,外部函数文字将使用其自己的作用域和局部variablesnum (其值设置为i的当前值)计算为新的函数对象。 由于num永远不会被修改,它将在闭包的生命周期中保持不变:下一个迭代步骤不会覆盖旧值,因为函数对象是独立的。

请记住,这种方法效率不高,因为必须为每个链接创build两个新的函数对象。 这是不必要的,因为如果您使用DOM节点进行信息存储,它们可以轻松共享:

 function linkListener() { alert(this.i); } function addLinks () { for(var i = 0; i < 5; ++i) { var link = document.createElement('a'); link.appendChild(document.createTextNode('Link ' + i)); link.i = i; link.onclick = linkListener; document.body.appendChild(link); } } 

我喜欢为厚厚的人写简单的解释,因为我很厚,所以在这里…

我们有5个div在页面上,每个ID … div1,div2,div3,div4,div5

jQuery可以做到这一点…

 for (var i=1; i<=5; i++) { $("#div" + i).click ( function() { alert ($(this).index()) } ) } 

但真正解决这个问题(慢慢地build立起来)…

步骤1

 for (var i=1; i<=5; i++) { $("#div" + i).click ( // TODO: Write function to handle click event ) } 

第2步

 for (var i=1; i<=5; i++) { $("#div" + i).click ( function(num) { // A functions variable values are set WHEN THE FUNCTION IS CALLED! // PLEASE UNDERSTAND THIS AND YOU ARE HOME AND DRY (took me 2 years)! // Now the click event is expecting a function as a handler so return it return function() { alert (num) } }(i) // We call the function here, passing in i ) } 

简单的了解替代

如果你不能得到你的头,那么这应该是更容易理解,并具有相同的效果…

 for (var i=1; i<=5; i++) { function clickHandler(num) { $("#div" + i).click ( function() { alert (num) } ) } clickHandler(i); } 

如果你记得调用函数时设置了一个函数variables的值,这应该是很容易理解的(但是它使用了和以前完全相同的思维过程)

基本上,在第一个示例中,您将onclick处理程序中的i直接绑定到onclick处理程序之外的i 。 所以,当onclick处理程序外部的i发生更改时, onclick处理程序中的i也会发生更改。

在第二个示例中,不是将其绑定到onclick处理程序中的num ,而是将其传递给一个函数,然后将其绑定到onclick处理程序中的num 。 当你把它传递给函数时, i的值被复制,而不是绑定num 。 所以当i改变时, num保持不变。 复制发生是因为JavaScript中的函数是“闭包”,这意味着一旦某个东西被传入函数,它就会被“closures”以进行外部修改。

其他人已经解释了发生了什么,这是一个替代解决scheme。

 function addLinks () { for (var i = 0, link; i < 5; i++) { link = document.createElement("a"); link.innerHTML = "Link " + i; with ({ n: i }) { link.onclick = function() { alert(n); }; } document.body.appendChild(link); } } 

基本上可怜的男人让绑定。

在第一个例子中,您只需将此函数绑定到onclick事件:

 function() {alert(i);}; 

这意味着在点击事件js应该警告addlink函数ivariables的值。 由于for循环(),它的值将是5。

在第二个示例中,您将生成一个要与另一个函数绑定的函数:

 function (num) { return function () { alert(num); }; } 

这意味着:如果调用一个值,返回一个函数,将提醒input值。 例如调用function(3)将返回function() { alert(3) };

您可以在每次迭代中用值i调用此函数,因此您可以为每个链接创build单独的onclick函数。

重点是在第一个例子中,你的函数包含一个variables引用,而在第二个函数中,在外部函数的帮助下,你用一个实际值replace了引用。 这被称为闭包,大概是因为你在你的函数中“包含”一个variables的当前值,而不是保留对它的引用。