本文分析JavaScript异步循环中因变量共享导致的索引打印错误问题,解释setTimeout回调引用原始变量的机制,并展示通过自调用函数建立闭包的正确写法,确保每次循环捕获正确的变量值。
异步循环中的变量共享问题
在实现异步循环逻辑时,开发者常会遭遇意料之外的行为。
尝试编写一个异步方法,在每次循环迭代中打印当前的索引值。
<script>
for(var i = 0; i < 5; i++){
setTimeout(function(){
document.writeln(i);document.writeln("<br />");
},1000);
}
</script>
上述代码的实际运行结果如下:
5 5 5 5 5
问题原因分析
造成该现象的根本原因在于变量作用域。
每次定时器(timeout)触发时,其回调函数引用的是循环变量 i 的当前值,而非迭代时的副本。由于 for 循环使 i 递增到 5,随后所有 timeout 回调执行时读取的 i 均为 5。
基于闭包的解决方案
解决此问题的核心在于为每次迭代创建独立的变量副本。
有多种方式可以实现变量拷贝,其中最常见且推荐的做法是利用函数声明构建闭包,并将当前循环变量 i 作为参数传递给该函数。此处演示的是使用自调用函数(IIFE)的方式。
应用该修复方案后的代码运行效果如下:
<script>
for(var i = 0; i < 5; i++){
(function(num){
setTimeout(function(){
document.writeln(num);document.writeln("<br />");
},1000);
})(i);
}
</script>
输出结果
0 1 2 3 4