重访 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 的 onClick 事件。然而,当点击这些链接时,它们都显示“链接 5”。
这个问题的根本原因在于 Javascript 的函数级作用域。当循环完成时, i 变量保留值 5。这是因为 Javascript 函数在其词法环境中是封闭的,这意味着它们可以访问周围范围中定义的变量。
此问题的解决方法是引入一个闭包,捕获每次迭代的 i 的当前值:
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); } }
在此代码中,为每个链接创建一个新的函数对象。它有自己的作用域和一个局部变量 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); } }
通过将 i 值直接存储在 DOM 节点上,我们消除了对闭包的需要,从而提高了代码效率。
以上是为什么 Javascript 循环会导致事件处理程序出现意外行为?的详细内容。更多信息请关注PHP中文网其他相关文章!