Javascript 臭名昭著的循环问题?

标签 javascript closures

我有以下代码片段。

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 个链接,并将每个链接与警报事件绑定(bind)以显示当前链接 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段摘自here 。正如作者的解释所示,似乎闭包创造了魔力。

但是它是如何工作的以及闭包如何使其工作都超出了我的理解范围。为什么第一个不起作用而第二个起作用?谁能详细解释一下这个魔法?

最佳答案

Quoting myself有关第一个示例的解释:

JavaScript's scopes are function-level, not block-level, and creating a closure just means that the enclosing scope gets added to the lexical environment of the enclosed function.

After the loop terminates, the function-level variable i has the value 5, and that's what the inner function 'sees'.

在第二个示例中,对于每个迭代步骤,外部函数文字将计算为具有自己的作用域和局部变量 num 的新函数对象,其值设置为 的当前值>我。由于 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);
    }
}

关于Javascript 臭名昭著的循环问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34346768/

相关文章:

swift - 对闭包强引用循环感到困惑?

javascript - 如何将 css 类添加到 Ext4 中的树节点?

javascript - 如何将变量从 NodeJS 导入到 JS?

Javascript Closure 和 WebRTC 回调问题

javascript - 访问 Angular 工厂中的嵌套函数闭包

javascript - 使用 Onclick 赋值的 For 循环中的闭包

swift - 如何在 Swift 中使用匿名闭包?

javascript - 如何在 Moment.js 中区分一个月和一个月的第一天

java - 当您使用 gwt 打开一个通过 twitter 进行 oauth 的弹出窗口时,如何访问父窗口?

javascript - 使用 PHP/JQuery 将 XML 转换为 JSON - 响应应为 JSON