一篇文章,带你彻底了解for循环中let与var区别

100 阅读3分钟
这篇文章会详细的带你了解for循环变量声明let与var区别,需要你互动去思考,会举很多例子,请耐心看完。  

首先来看个简单的例子,思考以下代码的输出有没有区别,这里运用到的知识点是函数作用域和块级作用域。

for (let i = 0; i <3; i++) {
    console.log(i)
 }
for (var j= 0; j <3; j++) {
    console.log(j)
 }

这里输出是一样的 都是 0 ,1,2 ,但他并没有想象中的简单,我们来看一下他隐藏的知识点。

前提

for循环里的()做了特殊处理,我们可以将()声明的变量当作在{}里声明的,他们的作用域是一致的(这里的let会被{}所影响,因为let是块级作用域,var则不会,因为他是函数作用域)

这里的隐藏知识点:

当我使用let声明i时,他是在栈中声明了3个不同的变量,但是var声明变量时,他只声明了一个变量(只是赋不同的值);使用let时,console.log是引用了3个不同的i,使用var时,是引用同一个变量,这是因为let受块级作用域,所以每次都会在内存中生成一个新的变量,而var是受函数作用域影响,但是这里没有定义在函数中,所以它相当于全局变量,在外部也可以引用。

下面举个例子:

let,受块级作用域影响,在{}中独立存在,所以可以在{}中声明同名称的变量

{
        let i = 0
        console.log(i)     
}
{
        let i = 1
        console.log(i)     
}
{
        let i = 2
        console.log(i)     
}

var,是受函数作用域影响,但是目前只有块级作用域,所以定义在{}和外面是一样的

var j
{
         j = 0
        console.log(j)     
}
{
        j = 1
        console.log(j)     
}
{
        j = 2
        console.log(j)     
}

你可能会觉得这个知识点不重要,但是对于接下来的for循环中的异步是必须掌握的。

思考以下的例子,想一下输出什么

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

答案:输出3,3,3 你想到了吗

有的人可能恍然大悟,因为这是定时器啊,需要等待才能执行,i++已经先运行了所以输出3,3,3,那我再举个例子,我将定时器的时间设为0, 现在呢,答案是什么,你想到了吗

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

答案:3,3,3

这里的原因是因为setTimeout是个异步执行的,计时器会被挂起在事件队列,等待执行,而 for (var i = 0; i <3; i++)是同步的,所以执行完到3时,这里才会被捕获,这里运用到了之前的知识点,也就是var定义的变量等同于全局变量,所以console.log一直引用的是同一个。

这里我们将代码改良一下,这里直接运行我们的function,猜想一下结果

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

答案:3,3,3

这里直接运行结果也不改变我们的全局变量i的运行,这里的console.log(i)还是引用同一个i。

那我们在改良一下代码,让j=i,这里你需要细细的去想

for (var i = 0; i <3; i++) {
    (function(){
        var j =i
        setTimeout(() => {
            console.log(j)
       }, 1000);
    })()
 }

答案:0,1,2

你想到了吗?这里有的人会有疑问,不是说console.log引用同一个var 创建的变量吗,为什么会得出0,1,2 ,其实你仔细看,之前说过var是受函数作用域影响的,而此时var已经声明在了function中,所以他现在的展开就是

{
        var j =0
        console.log(j)     
}
{
        var j =1
        console.log(j)     
}
{
        var j =2
        console.log(j)     
}

看着是不是有点眼熟,是不是和let的例子一样,此时不在引用同一个变量了,j已经被var声明了3次,那我们将代码改良一下呢使用let来替换var,本质上我需要在不同的块级/函数作用域中声明不同的变量并引用,而不是引用同一个公共变量。

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

 现在看起来是不是就顺眼多了呢,之前说过for()里的let是相当于定义在{}中的,所以相当于在每一个块级作用域中,我们都声明了一个let i ,并赋值,等待定时器挂起引用。

这篇文章其实一直在运用闭包的知识点,我会在其他文章中单独阐述。

如果这篇文章帮到了你,希望你点个赞,支持一下 谢谢。