JavaScript闭包访问外部变量的陷阱与解决方案

作者:闭包旅人

时间:2026-10-28

浏览:660

本文深入解析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值各不相同。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章