"Javascript恶名昭彰的循环问题?"
"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); } }
以上2个代码片段摘自这里。由于作者的解释似乎闭包使得这个代码有着神奇的效果。
但是它是怎么工作的,闭包如何使它工作,这一切都超出了我的理解范围。为什么第一个不起作用,而第二个起作用?有人可以详细解释一下这个魔术吗?
admin 更改状态以发布 2023年5月22日
我们页面上有5个div,分别有ID为...div1, div2, div3, div4, div5
jQuery可以做到这一点...
for (var i=1; i<=5; i++) { $("#div" + i).click ( function() { alert ($(this).index()) } ) }
但是真正解决问题的方法(并慢慢构建它)是...
步骤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); }
如果你记得函数的变量值是在函数调用时设置的,那么这应该很容易理解(但这使用了与之前相同的思考过程)
引用自己对第一个例子进行解释:
JavaScript的作用域是函数级别的,而不是块级别的。创建闭包只意味着封闭的范围被添加到封闭函数的词法环境中。
循环终止后,函数级变量i的值为5,这就是内部函数看到的值。
在第二个例子中,对于每个迭代步骤,外部函数文字将评估一个具有其自己范围和本地变量num
的新函数对象,其值设置为i
的当前值。由于num
从未被修改,因此它将在闭包的生命周期内保持不变:下一个迭代步骤不会覆盖旧值,因为函数对象是独立的。
请记住,这种方法非常低效,因为每个链接必须创建两个新的函数对象。如果您使用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); } }