JavaScript闭包访问外部变量的陷阱与解决方案
本文深入解析JavaScript闭包在访问外部变量时的常见陷阱,即所有闭包共享同一变量引用导致获取到最终值的问题。通过对比错误代码与使用立即执行匿名函数捕获值的正确方案,结合按钮点击事件绑定的经典案例,详细演示如何利用参数按值传递特性解决闭包变量共享难题。
闭包访问外部变量的陷阱
所谓闭包,是指一个有权访问另一个函数作用域中变量的函数。但在作用域配置机制中,有一个关键点需要特别注意:闭包只能取得包含函数中任何变量的最后一个值。
以下案例展示了这一现象:
function create(){
var arr = new Array();
for (var i=0; i<10; i++){
arr[i] = function(){
return i;
};
}
return arr;
}
var c_arr = create();
for(var i=0; i<c_arr.length;i++){
document.write("c_arr["+i+"] = "+c_arr[i]()+"<br />");
}
执行结果如下:

从表面看,似乎每个函数返回的i值各不相同,例如c_arr[0]应当返回0,c_arr[1]应当返回1,依此类推。然而实际结果是每个函数都返回10。这是为什么呢?
原因在于每个函数的作用域链中都保存着create()函数的活动对象,因此它们引用的都是同一个变量i。当for循环结束后,i的值变为10,此时每个函数引用的都是保存变量i的同一个对象。
利用匿名函数修正闭包行为
我们可以通过创建另一个匿名函数来强制让闭包的行为符合预期,使每个位置对应相应的值。
function create(){
var arr = new Array();
for (var i=0; i<10; i++){
arr[i] = function(num){
return function(){
return num;
};
}(i);
}
return arr;
}
var c_arr = create();
for(var i=0; i<c_arr.length;i++){
document.write("c_arr["+i+"] = "+c_arr[i]()+"<br />");
}
执行结果如下:

这里定义了一个匿名函数并立即执行,将结果赋给数组。该匿名函数有一个参数num,即最终函数要返回的值。在调用每个函数时,我们传入变量i。由于函数参数是按值传递的,因此会将变量i的当前值赋值给参数num。而在该匿名函数内部,又创建并返回了一个访问num的闭包。这样一来,arr数组中的每个函数都拥有自己num变量的副本,从而能够返回各自不同的数值。
按钮点击事件中的闭包应用
经典例子
再看一个经典案例:假设页面有一组button标签,我们利用脚本为这组按钮绑定单击事件,并在点击时弹出这是第几个标签。
<meta charset="utf-8" />
<button>第一个</button>
<button>第二个</button>
<button>第三个</button>
<button>第四个</button>
<script type="text/javascript">
var obj = document.getElementsByTagName('button');
for(var i=0;i<obj.length;i++){
obj[i].onclick = function(){
alert(i);
};
}
</script>
点击每一个按钮的结果:

表面上看,单击每一个标签似乎应该弹出不同的数字。
第一个按钮应该弹出0;
第二个按钮应该弹出1;
以此类推。
但实际结果是所有按钮都弹出4,这显然不是我们期望的结果。
我们将程序修改如下:
<meta charset="utf-8" />
<button>第一个</button>
<button>第二个</button>
<button>第三个</button>
<button>第四个</button>
<script type="text/javascript">
var obj = document.getElementsByTagName('button');
for(var i=0;i<obj.length;i++){
obj[i].onclick = function(num){
return function(){
alert(num);
}
}(i);
}
</script>
点击第二个按钮的结果:

点击第四个按钮的结果:

我们只需在函数内部建立一个匿名函数,原理与上述案例相同。这样即可实现匿名函数捕获外部变量i,使得每个按钮弹出的i值各不相同。
什么时候该用JS构造器:与工厂模式、字面量的选择对比
2026-11-05 16:18:22
推荐几款轻量级JS照片墙开源库及对比
2026-11-05 15:56:19
JavaScript递归写法有哪些常见陷阱及避免方法
2026-11-05 14:53:38
JS数组浅拷贝与深拷贝怎么选?场景与方法总结
2026-11-05 14:41:02
p5.js 中 setup() 和 draw() 函数的执行机制详解
2026-11-05 12:32:38
