循环中的闭包

首先看一段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]()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值