addEventListener、for()、index。如何使用闭包?

11

我有这段代码:

var items = this.llistat.getElementsByTagName('a');

for( var i = 0; i < items.length; i++ ){    
  items[i].addEventListener('click', function(event) {
    alert( i );
  }, items[i]);
}

事件被监听,但有 3 个项目,而且警报始终在任何元素上打印 3(它不尊重索引),

items[i] 不应该作为闭包完成任务吗?

谢谢!


addEventListener 的第三个参数是一个布尔值,用于指示事件监听器是否具有捕获优先级(例如,使其可取消);它不指定 this 值。 - apsillers
也与之相关,Javascript 的臭名昭著的循环问题? - Jonathan Lonowski
2个回答

12

不,addEventListener 的第三个参数是 useCapture。请查看MDN获取更多信息。

但你可以使用:

for( var i = 0; i < items.length; i++ ){
    (function(i){
        items[i].addEventListener('click', function(event) {
            alert( i );
        }, false);
    })(i);
}
var handler = function(event) {
    var i = items.indexOf(this);
    alert( i );
};
for( var i = 0; i < items.length; i++ ){
    items[i].addEventListener('click', handler, false);
}

第一个为每个元素创建一个新的事件处理程序,因此需要更多内存。第二个重复使用相同的事件监听器,但使用了 indexOf,因此速度较慢。


9

这是一个经典的闭包问题: 你必须创建一个新的函数绑定,而不是绑定到变量 'i',而是绑定到绑定时的值:

var items = this.llistat.getElementsByTagName('a');

for( var i = 0; i < items.length; i++ ) {
        items[i].addEventListener('click', listener.bind( null, i) );
}

function listener(index) {
         alert(index);
}

谢谢您的回答,这个方法可行。只使用匿名函数是否可能?只是好奇..谢谢! - Toni Michel Caubet
不客气。存储在listener内部的函数是匿名的。您可能不想创建此中间变量,而只需在addEventListener内部用其值替换'listener'。但我认为这样更容易理解。 - GameAlchemist
JavaScript 解释器不会进行优化,因此最好在循环外定义 function listener(index) { return function() {...}; },然后在循环内使用单个语句 items[i].addEventListener('click', listener(i));。效率的提升来自于只定义一次外部函数。如上所述,外部函数在每次循环迭代时都会被定义(和执行)。 - Beetroot-Beetroot
@Beetroot-Beetroot:我进行了编辑,但更多是为了清晰而不是性能:单击处理程序无论如何都很慢,但是现在每个元素只会创建一个函数,而不是两个,这将节省几个纳秒 :-) - GameAlchemist

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