在面试题中我们常常会遇到一个与循环、闭包有关的面试题。如下例。

// 利用闭包的知识,修改这段代码,让代码的执行结果为隔秒输出1,2,3,4,5
for (var i=1; i<=5; i++) {
    setTimeout( function timer() {
        console.log(i);
    }, i*1000 );
}

我们首先来分析一下如果直接运行这个例子会输出什么结果。

前面我们已经知道,for循环的大括号并不会形成自己的作用域,因此这个时候肯定是没有闭包产生的,而i值作为全局的一个变量,将会随着循环的过程递增。因此循环结束之后,i变成6。

而每一个循环中,setTimeout的第二个参数都是访问的是当前的i值,因此第二i值分别是1, 2, 3, 4, 5。而第一个参数timer函数中虽然也访问的是同一个i值,但是由于延迟的原因,当timer函数被setTimeout运行时,循环已经结束,i已经变成了6。

因此这段代码直接运行的结果会隔秒输出6。

而我们想要达到隔秒输出1, 2, 3, 4, 5的目的,那就得借助闭包的特性,将每一个i值都用一个闭包保存起来。每一轮循环,就将当前的i值保存在一个闭包中,当setTimeout中定义的操作执行时,则访问对应的闭包即可。

这个时候我们脑袋里过一遍闭包形成的条件,简单来说就是一个函数里定义一个子函数,子函数内访问函数的变量对象。因此我们只需要创建出这样的环境即可。

修改代码如下:

for (var i=1; i<=5; i++) {
    (function(i) {
        setTimeout( function timer() {
            console.log(i);
        }, i*1000 );
    })(i);
}

定义一个匿名函数,称作A,并将其当做闭包的环境。而timer函数则作为A的内部函数,当A执行时,只需访问到A的变量对象即可。因此将i值作为参数传入,这样也就满足了闭包的条件并将i值保存在了A中。

同样的道理,也可以在timer函数这里做文章,如下:

for (var i=1; i<=5; i++) {
    setTimeout((function(i) {
        return function timer() {
            console.log(i);
        }
    })(i), i*1000 );
}

results matching ""

    No results matching ""