var和let
两者都是指对变量声明,由于var的用法会造成很多bug,故ES6新增了let。
var
var表示声明函数作用域的变量,var会有变量提升,将变量提到作用域的前方。
以下面JS代码为例:1
2
3
4
5
6var a=1
function foo(){
alert(a)
var a =2
}
foo.call()
初学者我们都会直接用眼睛去看代码,以为alert的结果为1,实际不是的。浏览器会分两次看上面的代码,第一次会找声明:
1 | var a |
到第二次,就会真正地看代码:1
2a = 1
foo.call()
当浏览器解析到foo.call()就会去看那个要调用foo函数,同样浏览器会看两次:1
2
3
4
5//第一次
var a
//第二次
alert(a)
a = 2
将以上代码层层分析,不难知道a只声明了,并为赋值,所以alert的结果为undefined。
let五大特点
1. let是块级作用域的变量声明。
1 | let a =1 |
第一个let作用既包括了括号外面的也包括了花括号里面的,第二个let的作用域只在花括号里。
2. 不得在同一作用域里let声明变量两次,否则会报错。

3. let也有变量提升
4. let的TDZ
TDZ的意思是Temp Dead Zone(临时死亡区域),由于let有变量提升,会提升到当前作用域的第一行,其中,实际声明行与作用域的第一行之间的区域叫TDZ。1
2
3
4
5let b=1
{
console.log(b)
let b=2
}
这样会有语法报错。
5. let与for循环作用时会有奇效
1 | for(var i=0;i<6;i++){ |
首先以var声明来看,浏览器对于这段代码依旧分两次来看,第一次:var i,第二次for(i=0;i<6;i++){setTimeout(匿名函数,1000}},匿名函数是不算做声明的。由于由于for循环由开始至结束,时间非常短,而定时器还未触发,如下图:
由于for循环早早结束,那么在1s前i的值已经变为了6,等时间到了1s,那六个‘闹钟’会依次被打开(Quick),最终都发现i已经变为了6,所以控制台会打出6个数值6。
当我们把上面代码的var改为let后,控制台会打出什么呢?1
2
3for(let i=0;i<6;i++){
setTimeout(function(){console.log(i)},1000)
}
结果是:
为什么会这样?
这里我们要说明的是,let的声明既不在外面,也不在花括号里,而是在圆括号里(呆在原地)。根据ECMAScript规则:
如果for循环和let一起使用时,那么let在每次进入花括号之前,会帮你声明(复制)一个i,会给这个块一个同名的变量。
每次i递增时,会把i的值放到for循环里,所以每次控制台打出的i值是不同的。