在Javascript循环中使用事件处理程序 - 需要闭包吗?

36

我正在处理一些从别人那里接手过来的 HTML 和 JavaScript 代码。该页面每十秒钟重新加载一张数据表(通过异步请求),然后使用一些 DOM 代码重建表格。相关代码如下:

var blah = xmlres.getElementsByTagName('blah');
for(var i = 0; i < blah.length; i++) {
    var td = document.createElement('td');
    var select = document.createElement('select');
    select.setAttribute("...", "...");
    select.onchange = function() {
        onStatusChanged(select, callid, anotherid);
    };
    td.appendChild(select);
}

当对于一个<select>元素触发onchange事件时,似乎相同的值会被传递到表格中每个<select>onStatusChanged() 方法中(我已经验证在每次循环迭代中,callidanotherid都有新的、不同的值)。

我怀疑这是因为我设置事件处理程序的方式的本质所致,使用select.onchange = function()语法。如果我理解正确,这个语法设置了一个闭包,使得 onchange 事件成为一个引用这两个引用的函数,这两个引用最终的值是在循环的最后一次迭代中它们被设置为什么就是什么。当事件触发时,由callidanotherid引用的值是在最后一次迭代中设置的值,而不是在单个迭代中设置的值。

有没有一种方法可以复制我传递给onStatusChanged()的参数的值?

我已经更改标题以更好地反映问题和接受的答案。

2个回答

50

你确实需要在这里实现一个闭包。这个应该可以工作(让我知道 - 我没有测试过)

var blah = xmlres.getElementsByTagName('blah');
for(var i = 0; i < blah.length; i++) {
    var td = document.createElement('td');
    var select = document.createElement('select');
    select.setAttribute("...", "...");
    select.onchange = function(s,c,a)
    {
        return function()
        {
            onStatusChanged(s,c,a);
        }
    }(select, callid, anotherid);
    td.appendChild(select);
}

另外一个解决方案我发现 - 一种变通的方法,就是将callid和anotherid作为属性存储在DOM元素中,在事件处理程序内部只需通过this.getAttribute()引用这些属性。 - matt b
我不会称它为解决方法。我的期望是只设置一次处理程序(而不是每次循环),并让它自己发现变量。 - dkretz
请注意,OP拥有的也是一个闭包。事实上,OP拥有更具代表性的形式,其中您的闭包与其他闭包共享其环境(每次循环运行创建一个闭包)。但这正是导致意外效果的原因。 - Jo So

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接