for循环中var和let的区别

今天在做题的时候,遇到了一个不太懂的问题,经过查阅资料,终于搞明白了这个问题,发出来当做个笔记。

首先是var:

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

            setTimeout(function() {

                console.log(i);

            })

        };

输出结果:for循环中var和let的区别_第1张图片

let方法:

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

            setTimeout(function() {

                console.log(i);

            })

        };

输出结果:

for循环中var和let的区别_第2张图片

可以看到,var方法输出结果为5个5,let方法输出结果为01234,那么为什么会出现这种情况呢,主要原因就是因为setTimeout是异步执行的。

首先我们来了解下let和var的区别:

for循环中var和let的区别_第3张图片

        然后这道题涉及到了异步,作用域还有闭包。

        在var里面:

        setTimeout是异步执⾏,10ms后往任务队列⾥⾯添加⼀个任务,只有主线线上的全部执⾏完,才会执⾏任务队列⾥的任务,当主线执⾏完成后,i是5,所以此时再去执⾏任务队列⾥的任务时,i全部是5了。对于打印5次是:

         每⼀次for循环的时候,settimeout都执⾏⼀次,但是⾥⾯的函数没有被执⾏,⽽是被放到了任务队列⾥⾯,等待执⾏,for循环了5次,就放了5次,当主线程执行完成后,才进入任务队列里面执行。

        在let里面:

        因为for循环头部的let不仅将i绑定到for循环中,事实上它将其重新绑定到循环体的每一次迭代中,确保上一次迭代结束的值重新被赋值。setTimeout里面的function()属于一个新的域,通过var定义的变量是无法传入到这个函数执行域中的,通过使用let来声明块变量能作用于这个块,所以function就能使用i这个变量了。

        注意:

        由于var命令的变量提升机制,var命令实际只会执行一次,而let命令不存在变量提升,所以每次循环都会执行一次,声明一个新变量(但初始化的值不同),for的每次循环都是不同的块级作用域,let声明的变量也是块级作用域,所以也不存在重复声明的问题,let声明变量的for循环里,每个匿名函数实际上引用的都是一个新的变量。

        当然,如果要使用var当做循环循环头的话,出现循环后打印出的结果一模一样的问题,可以使用闭包来解决。

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

            (function(i) {

                setTimeout(function timer() {

                    console.log(i);

                });

            })(i);

        }

输出结果:

for循环中var和let的区别_第4张图片

        通过闭包,将i的变量驻留在内存中,当输出i时,引用的是外部函数的变量值i,i的值是根据循环来的,执行setTimeout时已经确定了里面的的输出了。 

你可能感兴趣的:(javascript)