You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

循环绑定addEventListener:为何返回函数的写法有效?两种写法解析

为什么版本1无效而版本2有效?还有更优方案吗?

这个问题的核心在于JavaScript的闭包机制和var的作用域特性,咱们一步步来理清楚:

版本1为什么无效?

你以为var tmp_i = i是复制了当前循环的i值,但实际上var声明的变量是函数级作用域(这里因为没有包裹函数,就是全局作用域),而不是块级作用域。也就是说,整个循环里只有一个tmp_i变量,每次循环都会把它重新赋值为当前的i。

当点击事件触发时,循环早已经执行完毕了,tmp_i最后被赋值为循环的最大值(也就是2,因为数组长度是3,i从0跑到2),所以不管点击哪个div,都会弹出这个最终值。事件回调里引用的是这个tmp_i变量的引用,而不是循环时的快照值。

版本2为什么有效?

这里用到了闭包的特性:每次调用my_function_creator(i)时,都会创建一个独立的函数作用域,参数arg_i会被绑定到这个作用域里。返回的匿名函数在点击触发时,会访问自己上层作用域的arg_i——这个arg_i是当前循环传入的具体值,不会被后续循环覆盖,因为每次调用my_function_creator都生成了一个全新的作用域,保存了当时的i值。

简单说,这个函数工厂为每个点击事件回调都“锁住”了当前循环的索引值。

有没有更优的方案?

当然有!ES6之后的语法让这个问题变得简单很多,不需要手动写闭包工厂:

方案1:用let声明循环变量

把循环里的var i改成let i,let是块级作用域,每次循环都会创建一个新的i变量,事件回调会引用当前循环的那个i:

var myarray = ["House", "Car", "Fruit"];
for(let i=0; i<myarray.length; i++) {
    var mydiv = document.createElement("div");
    mydiv.innerHTML = myarray[i];
    document.body.appendChild(mydiv);
    mydiv.addEventListener("click", function(){alert("Version 3: You clicked on div nb " + i)}, false);
}

方案2:用forEach遍历数组

forEach的回调函数会为每个元素创建独立的作用域,直接用回调里的index参数就可以:

var myarray = ["House", "Car", "Fruit"];
myarray.forEach(function(item, index) {
    var mydiv = document.createElement("div");
    mydiv.innerHTML = item;
    document.body.appendChild(mydiv);
    mydiv.addEventListener("click", function(){alert("Version 4: You clicked on div nb " + index)}, false);
});

这两种写法都比版本2更简洁易读,是现在开发中更推荐的做法。

内容的提问来源于stack exchange,提问作者tobiasBora

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:28:24