提问人:Zhu Tao 提问时间:9/20/2009 最后编辑:Sabito stands with UkraineZhu Tao 更新时间:11/11/2021 访问量:74073
Javascript 臭名昭著的 Loop 问题?[复制]
Javascript infamous Loop issue? [duplicate]
问:
我有以下代码片段。
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 个片段引用自此处。正如作者的解释所言,闭合使魔术变得神奇。
但是它是如何工作的,以及闭包是如何使它工作的,都超出了我的理解。为什么第一个不起作用,而第二个有效?谁能详细解释一下这个魔法?
答:
在第一个示例中,只需将此函数绑定到 onclick 事件:
function() {alert(i);};
这意味着在单击事件中,js 应该提醒 addlink 函数 i 变量的值。由于 for loop(),它的值将为 5。
在第二个示例中,您将生成一个函数以与另一个函数绑定:
function (num) {
return function () { alert(num); };
}
这意味着:如果使用值调用,请返回一个函数,该函数将提醒输入值。例如,calling将返回。function(3)
function() { alert(3) };
在每次迭代时,您都使用值 i 调用此函数,因此您可以为每个链接创建单独的 onclick 函数。
关键是,在第一个示例中,您的函数包含一个变量引用,而在第二个示例中,借助外部函数,您将引用替换为实际值。这被称为闭包,大致是因为您将变量的当前值“包含”在函数中,而不是保留对它的引用。
引用我自己对第一个例子的解释:
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);
}
}
评论
data-something
link.i = i
link.setAttribute("data-link-index",i)
alert(this.i)
alert(Number(this.getAttribute("data-link-index")))
基本上,在第一个示例中,您将内部处理程序直接绑定到处理程序的外部。因此,当处理程序的外部发生变化时,内部处理程序也会发生变化。i
onclick
i
onclick
i
onclick
i
onclick
在第二个示例中,不是将其绑定到 in 处理程序,而是将其传递到函数中,然后函数将其绑定到 in 处理程序。当您将其传递到函数中时,会复制 的值,而不是绑定到 。因此,当发生变化时,保持不变。之所以发生复制,是因为 JavaScript 中的函数是“闭包”,这意味着一旦某些东西被传递到函数中,它就会被“关闭”以供外部修改。num
onclick
num
onclick
i
num
i
num
评论
其他人已经解释了发生了什么,这是一个替代解决方案。
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);
}
}
基本上是穷人让束缚。
评论
我们在页面上有 5 个 div,每个 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);
}
如果您还记得在调用函数时设置了函数变量值,这应该很容易理解(但这使用与以前完全相同的思维过程)
评论
clickHandler
上一个:如何使用异步闭包来捕获和接受引用
评论