首先看一段JS代码
function F(){
var arr =[],i;
for(i=0;i<3;i++){
arr[i] = function(){
return i;
}
}
return arr;
}
在控制器中执行
>var arr = F()
>arr[0]()
>arr[1]()
>arr[2]()
当我们看到这里的时候肯定会觉得这输出的不就是每次循环i的值么,但是我们在控制台里面运行后的结果如下
我们发现结果并不是我们想的那样简单,而是所有的值都为3了
这就是JavaScript中常见的闭包的作用
原因分析:
我们创建了一个函数F对象,里面有数组arr和一个局部变量i,执行for循环,定义arr数组里面的每个元素为一个函数对象,函数返回值为局部变量i的值,函数对外部闭包域的私有变量进行了引用,故 每个arr[]对象的 function scope 会产生一个名为 closure 的对象属性,closure 对象内含有一个名为 i 的引用,当执行F()时,实际上i的值已经变成了3,当执行arr时,每个函数里面的i是对局部变量i的引用,而此时的i已经变了所以引用的i的值全是3而不是对应的预期的值
我在搜索js闭包问题时,看到segmentfault上有位朋友讲了9种解决办法,觉得很有启发,就看着能不能举一反三,根据他的思路解决我们的这个问题,
具体的链接为多种方法解决循环中的闭包问题
解决办法
方法一
我在下面的for循环里面增加了一个闭包域空间,用arg来保存传进来的i,只要arg不变那么return 的就是相对应的值
function F(){
var arr =[],i;
for(i=0;i<3;i++){
(function(arg){
arr[i] = function(){
return arg;
}
}(i))
}
return arr;
}
var arr = F();
arr[1]()
方法二
这种方法和第一种有些类似,但又不同
不同点:解决办法一是在新增的匿名闭包空间内完成数组的函数绑定,而此例是将返回函数在新增的匿名函数返回的函数上 此时绑定的函数中的 function scope 中的 closure 对象的 引用 arg 是指向将其返回的匿名函数的私有变量 arg
function F(){
var arr =[],i;
for(i=0;i<3;i++){
arr[i] = (function(arg){
return function(){
return arg;
};
}(i))
}
return arr;
}
var arr = F();
arr[1]()
方法三
这种方法和第一种相似,都是新增了一个闭包域,返回的是新的闭包域中的私有变量
function F(){
var arr =[],i;
for(i=0;i<3;i++){
(function(){
var temp = i;
arr[i] = function(){
return temp;
}
}())
}
return arr;
}
var arr = F();
arr[1]()
方法四
function F(){
var arr =[],i;
for(i=0;i<3;i++){
arr[i] = (function(){
var temp = i;
return function(){
return temp;
}
}())
}
return arr;
}
var arr = F();
arr[1]()
方法五
function F(){
var arr =[],i;
for(i=0;i<3;i++){
(arr[i] = function(){
return(arguments.callee.i);
}).i = i;
}
return arr;
}
var arr = F();
arr[1]()
方法六
这种办法是给每个数组对象 new一个function对象,每个对象都有自己的作用域,通过 new 使用 Function 的构造函数 创建 Function 实例实现,由于传入的函数体的内容是字符串,所以Function 得到的是一个字符串拷贝,而没有得到 i 的引用(这里是先获取 i.toString()然后与前后字符串拼接成一个新的字符串,Function 对其进行反向解析成 JS 代码)
function F(){
var arr =[],i;
for(i=0;i<3;i++){
arr[i] = new Function("return "+i);
}
return arr;
}
var arr = F();
arr[1]()
方法七
这里和上面的一种办法和大的区别就是上一种办法使用了new关键字,结果是Function()函数作为了构造器函数
而这里没有new关键字,则是将Function()作为了一个函数进行调用,然后再函数内部自己生产了一个实例进行返回
function F(){
var arr =[],i;
for(i=0;i<3;i++){
arr[i] = Function("return "+i);
}
return arr;
}
var arr = F();
arr[1]()