循环中的闭包

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就会是不同的值了

下面的形式也可以:

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

参考:《JavaScript面向对象编程指南》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值