for循环中使用setTimeout函数,最后得到的结果不是预想中的结果,怎么用闭包解决。
之前开发中也遇到过这种问题,无论是用for还是while都会变成i+1,然后执行i次函数,于是用

    while(1){
        setTimeout(() => {
           console.log(i) 
        }, 1000);
    }

预想中应该是每隔一秒打印一次,运行时发现控制台根本不执行,并且会超时,如果仅从这里判断的话可能会得出结论,那就是for循环的机制是将所有循环中的函数加入任务队列中,但是事实上并不是循环的问题,尝试代码如下

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

最后的打印结果和用for循环是一样的,后面看到一篇博客写到settimeout时解释是因为js是单线程的,所以settimeout会延迟一段时间执行,settimeout的第二个参数如果设为0也不能立即执行,默认最小延迟是4ms,不能马上执行的settimeout会进入任务队列中,当主函数for循环结束的时候,i已经被更改成5,并且i是全局变量,在settimeout中的函数并没有创建副本,所以最后输出的结果是5个5。
所以这个问题始终是一个作用域问题,为了解决这个问题,推荐的做法是IIFE。

平时所见的“闭包的写法”这种说法,其实是IIFE(Immediately Invoked Function Expression)立即执行函数表达式,闭包是一种思想,为了能让代码在某些作用域中执行,变量也只在某些作用域中有效,这是目的,IIFE在内的所有写法都是手段。
在js中如果一个语句被()包裹的话,这就会被解释成一个表达式
请输入图片描述
一个函数被()包裹变成表达式,如果在函数后面加上()就代表立即执行这个函数,一旦函数被执行,就必定会形成自己的作用域,这就达到了闭包的效果。
请输入图片描述
还有一种更加保险的写法:

    (function(){})()//我的习惯写法

    (function(){}())//社区的推荐写法

7.30更新

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