一道经典的JS关于闭包的题

for(var i = 0; i < 5; i++) {
    (function() {
        setTimeout(function() {
            console.log(i);
        }, i * 1000);
    })(i);
}
问输出什么?

我其实是想问的
是不是可以拆一下,拆成

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

原题自己跑过了,输出的是5个5
是不是可以理解为:因为根本没有i传入到块级作用域里面去,所以里面跑的还是外面跑完之后的i?
不知道上面的理解的对不对,求大佬指点一下,看了一晚上闭包,看的要死啦....
求大佬推荐IIFE方面的文章,看了好几篇都感觉理解不了闭包和IIFE。

阅读 3.8k
6 个回答

第一:因为i没传进去

第二:拆成那样也行,但是你的setTimeout英文掉了个u..,简化一下可以写成如下:

  for (var i = 0; i <= 5; i++) {
        ((i) => setTimeout(() => console.log(i), i * 1000))(i);
    }

第三:关于IIFE,给你道题目做吧。。。

题目:请将下列代码至少使用【两种】方法修改,让调用时输出"小明"(调用部分不能更改)

    var name = "老王";
    var person = {
        name: "小明",
        whoName: function() {
            console.log(this.name);
        },
        tellMe: function() {
            var that = this;
            (function(callback) {
                callback();
            })(that.whoName);
        }
    };
    person.tellMe()//此处不能修改

顺便,你大概不是没理解闭包,而是没理解【匿名函数的作用域】

setTimeout()函数设置为异步函数,即不会阻塞语句继续执行,它将传入的函数在 i*1 秒后放入执行队列中,如果执行队列中没有别的任务,就会直接执行该函数;如果还有别的,就会先执行前面的任务,再执行该函数。由于该循环i由1到5的时间可以忽略不计,因此执行函数的时候i已经变为5了,所以就是每1s输出一个5.

如果在该循环下面在加一段代码,当循环时间很长时(大于5s,电脑性能好的话可以加大循环的次数),结果就是先输出test,同时输出5个5.

for(var i = 0; i < 5; i++) {

(function() {
    setTimeout(function() {
        console.log(i);
    }, i * 1000);
})(i);

}

var j=0;

    while (j < 4000000000) {
    j ++;
}
console.log("test");
for(var i = 0; i < 5; i++) {
    (function(i) {        //加个i,把i传进去
        setTimeout(function() {
            console.log(i);
        }, i * 1000);
    })(i);
}

这样就等价于你第二种写法,第一种i没传进去,访问的就是全局变量i,等定时器执行的时候for循环已经结束,i=5

其实这个不止理解闭包,还要理解一下JS的单线程执行。

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

其一,原题循环里面是立即执行函数,因为函数没有形参i,所以函数作用域内包括setTimeout函数内的i都是向上到全局作用域来找;
其二,这段代码相当于直接循环执行了5个立即执行函数,每个立即执行函数给了一个setTimeout的任务,由于JS单线程的原因,这五个异步执行的setTimeout需要等待主线程执行完以后才开始调用,等主线程循环完,i已经变成5了,然后开始依次调用那五个setTimeout任务;
其三,你的拆写由于给了形参,在函数内i有了局部作用域,所以输出应该是01234吧,但是如果你要写闭包那么拆也是可以的。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题