JavaScript的addEventListener函数不如预期般工作

3

我正在研究一些常见的Javascript程序。下面的代码会向DOM添加4个按钮,并为每个按钮添加一个事件监听器:

for(var i =0;i<5;i++){
    var btn = document.createElement('button');
    btn.appendChild(document.createTextNode('Button' + i));

    //function 1
    (function(i){
     btn.addEventListener('click', function(){console.log(i)});
     })(i);

    //function 2 commented
    /*btn.addEventListener('click', (function(i){
     return function(){
     console.log(i);
     }
     })(i));*/
    
    document.body.appendChild(btn);
}

函数1和函数2都向按钮添加了事件监听器,并且工作得非常完美。我想知道,为什么下面的代码不起作用:

for(var i =0;i<5;i++){
    var btn = document.createElement('button');
    btn.appendChild(document.createTextNode('Button' + i));

    btn.addEventListener('click', function(){
        console.log('Clicked' + i);
    });
    document.body.appendChild(btn);
}

这段代码在每次按钮点击时仅记录数字 5。为什么会这样呢?我不明白为什么它不能为每个循环保留变量 i 的值。


1
在你的两个示例中,你创建了一个立即执行的闭包,因此 i 的本地值与预期结果相符,在第三个示例中,你等待循环完成,然后当你点击 i 的值时,它是迭代的最大值。 - ale
可能是[JavaScript循环内的闭包-简单实用的例子]的重复内容。(https://dev59.com/v3RB5IYBdhLWcg3wAjNH) - jisoo shin
2个回答

2

如果使用var,JavaScript没有块级作用域。因此,使用您的代码,您可以将var替换为let以获得“预期结果”:

for(let i =0;i<5;i++){
    var btn = document.createElement('button');
    btn.appendChild(document.createTextNode('Button' + i));

    btn.addEventListener('click', function(){
        console.log('Clicked' + i);
    });
    document.body.appendChild(btn);
}


1
当在处理for循环时向eventTarget添加addEventListener,它不会触发侦听器。它在稍后的阶段被触发,此时for循环已经执行完并更新了i的值。当您在for循环内创建一个函数时,它会创建一个闭包并绑定i的值。

好的,现在我明白了,但是为了更深入地了解,为什么事件监听器没有被触发,为什么它要等待for循环完成。我可以理解当外部调用API获取数据时,for循环不会等待响应...但是为什么在事件监听器的情况下也是如此呢? - Stacy J
1
“触发”与“添加”是不同的。在循环内,您正在将事件添加到目标中。但是当您单击它时,触发就会发生。 - brk

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