循环中的闭包

function F(){
    var arr = [],i;
    for(i = 0;i < 3;i++){
        arr[i] = function(){
            return i;
        };
    }
    return arr;
}
var arr = F();

上述for循环每迭代一次就创建一个arr的项,每一项中保存着一个函数,该函数返回当前作用域中的i的变量值,然后在F()中返回该数组,将该函数赋值就可以访问F()内部非变量,因此形成了一个闭包

执行arr[0](),arr[1](),arr[2]()查看结果的时候发现结果并不是我们想象的0,1,2

arr[0]();
3
arr[1]();
3
arr[2]();
3

原因:在for循环中创建了三个闭包,而它们都指向了一个局部变量i。但是,闭包并不会记录它们的值,它们所拥有的只是相关域在创建时的一个链接(即引用)。在这个列子中,变量i存在于定义这三个函数域中。对这三个函数中的任何一个而言,当要去获取某个变量时,它会从其所在的域开始逐级寻找那个距离最近的i的值。由于循环结束时i的值为3,所以这三个函数都指向了这个值

换句话说就是:for循环一次创建一个对i变量引用的闭包,但是闭包中不会保存当时的值,而只是一个对于i变量的引用,调用引用的值会随着本身的值得变化而变化,当调用三个函数时会逐级寻找最近的i的值,此时i的值为3

改正

换一种闭包的形式

function F(){
    var arr = [],i;
    for(i = 0;i < 3;i++){
        arr[i] = (function(x){
            return function(){
                return x;
            }
        }
        )(i);
    }
    return arr;
}
var arr = F();

结果:

arr[0]();
0
arr[1]();
1
arr[2]();
2

在这里,不再创建直接返回i的函数,而是将i传递给另一个即时函数。在该函数中,i被赋值给了局部变量x,这样一来,每次迭代中的x就会是不同的值了

若不传值进入闭包内,但是闭包内部对变量i进行了本地化,则就进行了声明,只是未对其赋值,所以调用结果为undefined

function F(){
    var arr = [],i;
    for(i = 0;i < 3;i++){
        arr[i] = (function(i){
            return function(){
                return i;
            }
        }
        )();//此处将外部变量传入闭包
    }
    return arr;
}
var arr = F();
arr[0]();
undefined//未传值进入,所以结果为undefined

若闭包内部变量没有进行变量”本地化操作”,则无论外部是否给予传值,闭包在本地内找不到该变量,便会向上级作用域中查找,若找到则输出该值,未找到则进行报错

传入外部变量

function F(){
    var arr = [],i;
    for(i = 0;i < 3;i++){
        arr[i] = (function(){//未本地化
            return function(){
                return i;
            }
        }
        )(i);
    }
    return arr;
}
var arr = F();
arr[0]();
3//向上查找变量i的值

不传入外部变量

function F(){
    var arr = [],i;
    for(i = 0;i < 3;i++){
        arr[i] = (function(){//未本地化
            return function(){
                return i;
            }
        }
        )();//不传入外部变量
    }
    return arr;
}
var arr = F();
arr[0]();
3//向上查找变量i的值

未找到变量

function F(){
    var arr = [],i;
    for(i = 0;i < 3;i++){
        arr[i] = (function(){
            return function(){
                return x;
            }
        }
        )(i);
    }
    return arr;
}
var arr = F();
arr[0]();
VM895:6 Uncaught ReferenceError: x is not defined
    at Array.<anonymous> (<anonymous>:6:24)
    at <anonymous>:14:7

下面的形式也可以:

function F(){
    var arr = [],i;
    for(i = 0;i < 3;i++){
        (function(j){
            arr[j] = function(){
                return j;
            };
        })(i);
    }
    return arr;
}
var arr = F();
arr[0]();
0
arr[1]();
1
arr[2]();
2

也可以不使用即时函数,而定义一个内部函数来实现相同的功能,要点是在每次迭代操作中,我们要在中间函数内将i的值”本地化”而不是当前作用域中的值

function F(){
    var arr = [],i;
    function binder(x){
        return function(){
            return x;
        };
    }
    for(i = 0;i < 3;i++){
        arr[i] = binder(i);
    }
    return arr;
}
var arr = F();
arr[0]();
0
arr[1]();
1
arr[2]();
2

若在内部函数中不将其本地化,则结果还是为3

function F(){
    var arr = [],i;
    function binder(){//未本地化
        return function(){
            return i;//向上层作用域中查找该变量的值
        };
    }
    for(i = 0;i < 3;i++){
        arr[i] = binder();
    }
    return arr;
}
var arr = F();
arr[0]();
3

但是只要有参数传入内部函数binder(),就不会再F()的作用域中寻找i,会首先在函数作用域中寻找

function F(){
    var arr = [],i;
    function binder(i){
        return function(){
            return i;
        };
    }
    for(i = 0;i < 3;i++){
        arr[i] = binder(i);
    }
    return arr;
}
var arr = F();
arr[0]();
0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值