闭包与循环

2017-08-21  本文已影响0人  xiaoguo16

先来一个例子:

function A(){
    var m=[];
    for(var i=0;i<5;i++){
        m[i]=function () {
            return i
        }
    }
    return m;
}
var a=A()[0]
a();//5

上面的例子中数组m中保存的函数返回值都为5。

why?

上述例子中的a是匿名函数,而执行匿名函数时,匿名函数保存着外层函数的活动对象的引用,而它们引用的都是外层函数中的i,也就是引用的同一个值,所以都是最后的变量i。

修改后:

function A(){
    var m=[];
    for(var i=0;i<5;i++){
        m[i]=function(j){
            return function () {
                return j;
            }
        }(i)
    }
    return m;
}
var a=A()[0]
a();//0

原理:

利用函数传参的特点,函数传参是按值传递的,而非按引用,所以j中保存的是i的副本,也就是每次的值都是不同的。

常见错误

for(var i=1;i<=5;i++){
    setTimeout(function(){
        console.log(i);
    },i*1000)
}
//输出5个6;

why?

Paste_Image.png

先运行主线程序,然后运行到setTimeout这个异步函数时,它会有一个延迟,此时该函数中的function丢入副线运行,主线继续执行,当执行完for循环后,此时在检查副线进程,发现副线已经到时间了,就传给它i值,但此时的i值已经变为6了,所以后面的function都是6。
但是这里即便延迟为0,仍然是输出5个6,因为它们对i的引用为同一个值。

解决方案

for(var i=1;i<=5;i++){
    (function(j){
        setTimeout(function(){
            console.log(j)
        },1000)
    })(i)
}
//1,2,3,4,5

利用函数的传参的性质,按值传递,所以j是i的副本,即分别为1,2,3,4,5。

上一篇下一篇

猜你喜欢

热点阅读