循环中的闭包

来源:互联网 发布:手机电影拍摄软件 编辑:程序博客网 时间:2024/06/07 12:28
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]();3arr[1]();3arr[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]();0arr[1]();1arr[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]();0arr[1]();1arr[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]();0arr[1]();1arr[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面向对象编程指南》

0 0
原创粉丝点击