var
声明的变量是没有块级作用域的:
eg:
{
var a = 10;
}
console.log(a); //a = 10
let
声明的变量具有块级作用域:
eg:
{
let a = 10;
}
console.log(a); //报错,a is not defined
下面我们来看一个常见的面试题:
for (var i = 0; i < 3; i++) {
console.log(i); //0 1 2
setTimeout(function(){
console.log(i); // 3 3 3
}, 0);
}
输出结果为什么会是这样的呢?
因为var声明的i是一个全局变量,第一个console.log(i)
是一个同步任务,所以在i依次被重定值时会一起变化,而在第一次循环时发现了定时器,由于它是属于异步任务,所以它不会马上执行,而是把它扔进了事件侦听模块,计时到了,就会把它放进任务队列
里去,等待同步任务执行完毕时再回头看任务队列里是否有任务,若有,则会执行,此时i早已变成了3了,第n次循环亦是如此…
这里可以参照同步和异步任务详解:https://segmentfault.com/a/1190000011198232
若想定时器里的i依次输出呢?我们有几种方法:
我们可以加个自执行函数
传个实参,这样可以把全局变量下的i变成局部变量。
for (var i = 0; i < 3; i++) {
console.log(i); //0 1 2
(function(i) {
setTimeout(function(){
console.log(i); // 0 1 2
}, 0);
})(i);
}
而下面把var变成let呢?
for (let i = 0; i < 3; i++) {
console.log(i); //0 1 2
setTimeout(function(){
console.log(i); // 0 1 2
}, 0);
}
结果得到我们期待的值,这是因为什么呢?
因为我们知道let声明的具有块级作用域,它现在就是一个局部变量,每一次的i声明都是单独的。
下面我们来详写这个过程:
var声明的i相当于执行如下操作:
{
var i = 0;
setTimeout(function(){console.log(i)}, 0) // i = 2
}
{
var i = 1;
setTimeout(function(){console.log(i)}, 0) // i = 2
}
{
var i = 2;
setTimeout(function(){console.log(i)}, 0) // i = 2
}
let:
{
let i = 0;
setTimeout(function(){console.log(i)}, 0) // i = 0
}
{
let i = 1;
setTimeout(function(){console.log(i)}, 0) // i = 1
}
{
let i = 2;
setTimeout(function(){console.log(i)}, 0) // i = 2
}
let声明的变量只在自己的块级区域有效,所以是单独存在的哟!!