循环中的闭包

来源:互联网 发布:fresh玫瑰化妆水知乎 编辑:程序博客网 时间:2024/06/05 00:40

首先看一段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
原创粉丝点击