var声明变量:
var只有函数作用域,没有块级作用域
//函数作用域的表现 function test(){ var i =10; console.log(i); //10 } test(); //10 console.log(i); // i is not defined; //块级作用域对var没有约束 { var i = 10; console.log(i); //10 } console.log(i); //10
从上面的代码可了解到,块级作用域对var是没有约束作用的。
let声明变量:
let与var不同,let是有块级作用域的。
//块级作用域对let声明的变量有约束 { let i = 10; console.log(i); //10 } console.log(i) //ReferenceError
了解了上面的特性再来看看,var和let在for循环的一些不同表现:
//var声明i function test(){ for(var i=0;i<2;i++){ setTimeout(()=>{ console.log(i); },0); } } test(); //输出结果:2、2 //let声明i function test(){ for(var i=0;i<2;i++){ setTimeout(()=>{ console.log(i); },0); } } test(); //输出结果:0、1
可以看到只是声明方式不一样,输出的结果却有很大的差异。
在此之前还需要了解setTimeout()的执行机制:
setTimeout()是以异步的方式执行的。在执行for循环的时候,并不是执行一次for循环就立刻执行一次setTimeout(),而会让setTimeout()进入另一条线程进行等待,当主线程(这里就是test())执行完后,setTimeout()再依次执行。
在var中执行的时候:
因为var是没有块级作用域的,所以在for循环中声明的i会存在于test()函数作用域中。每一次for循环就会声明一次i,但后面声明的变量会覆盖前面声明的变量。所以当for循环执行完后(此时setTimeout()还未被执行),函数作用域中的i的值就变成2了
而setTimeout()所在的线程中是这样的:
//第一次进栈 setTimeout(()=>{ console.log(i); }); //第二次进栈 setTimeout(()=>{ console.log(i); });
这里的i都指向函数作用域中的i。所以输出都为2。
在let中执行的时候:
因为块级作用域的原因,let声明的i都会存在于for块级作用域中,每一次for循环都会生成一个块级作用域。所以setTimeout()在线程中是这样的:
{ let i=0; setTimeout(()=>{ console.log(i); }); } { let i=1; setTimeout(()=>{ console.log(i); }); }
所以会一次输出0,1;