ES6在原有var关键字的基础上,增加了let和const两个关键字用来声明变量。新增加的关键字在使用上更为严谨,解决了很多原来var声明变量时造成的问题。
之前跟着Blue的ES6课程过了一遍ES6的基本语法,后来全栈课程里也听了一遍ES6,但是感觉还是不太系统,所以跟着阮一峰老师的《ECMAScript 6入门》又学了一遍,在此要强烈推荐阮一峰老师的这本ES6入门,真的非常细致。我也把笔记大致整理了一下,有一些高级的内容还没太明白,弄明白的我争取把每个内容都自己叙述一遍。
ES6较ES5在变量声明上增加了let 和 const两个新的关键字,用法上也有一些区别。我做了一个对比表格。
| 关键词 | 特性 | 作用域 | 变量提升 |
|---|---|---|---|
| var | - 声明变量 - 可以重复声明 - 后一次声明会覆盖前一次 |
函数级作用域 | 会提升 |
| let | - 声明变量 - 无法重复声明 |
块级作用域 | 不会提升 |
| const | - 声明常量 - 无法重复声明 - 声明必须赋值 - 可以修改对象属性 |
块级作用域 | 不会提升 |
1. 声明
var用于声明变量,且可以重复声明,后一次声明会覆盖前一次声明。
1 | var a = 12; |
let用于声明变量,const用于声明常量,两者都无法重复声明,否则会报错,其中,const声明常量时必须赋值
2. 变量提升 Hoisting
var声明变量时,会变量提升。
所谓变量提升,就是无论任何时候对变量赋值,都会在作用域头部给变量添加无赋值声明(无赋值很重要,之前弄错很多次)
1 | var a = 0; |
let和const不存在变量提升,经典面试问题如:
1 | let str = 'hello'; |
而如果是let和const,变量不会提升到头部,打印i就会出现未定义的问题。
1 | let str = 'hello'; |
3. 函数级作用域 VS 块级作用域
ES5中只有全局作用域和函数作用域,没有块级作用域。函数作用域是指在函数中定义的变量,函数外部不能获取变量。但由于var声明的变量存在变量提升,因此会导致一些问题。
问题1: 内部变量可能会覆盖外部变量
1 | let temp = new Date(); |
问题2: 循环变量会污染全局变量
1 | var str = 'hello'; |
ES6中新增了块级作用域。
1 | var str = 'hello'; |
let生成的str的值不会对输出结果产生任何影响,因为let声明的str的作用域是if内部的块级作用域。如果我们在if内部添加一个console.log(str),同时将前一个console.log(str)放到if后面会看得更明白,let声明的内容只在if内部起作用。
1 | var str = 'hello'; |
1 | var str = 'hello'; |
4. 暂时性死区(TDZ)
暂时性死区 Temporary Dead Zone
如果区块中使用了let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域
即声明之前,变量就已经存在,但是不可获取(使用)
1 | var str = 'hello'; |
还是上面一个例子,如果将if去掉,会发现输出结果变成了str is not defined,因为去掉if之后,函数内部会形成TDZ,防止在let之前,str被重复声明,因此console.log(str)实际在str被声明之前输出,结果是str未定义。
5. const修改属性
前面说过,const声明变量必须赋值,且一旦赋值将不能改变。本质上是变量指向的内存地址不能改变。
对于简单类型的变量来说,即不能改变值;
对于复合类型变量(如数组,json),可以修改符合类型变量的属性
1 | //数组 |
可以通过冻结对象(Object.freeze()方法)来避免const声明的复合类型变量被修改
1 | //数组 |
6. 顶层对象属性
ES5中,顶层对象属性和全局变量等价
1 | window.a = 1; |
ES6对这一现象进行了改进,保留了原有的var和function声明的全局变量是顶层对象属性。
但是let,const和class声明的全局对象将不属于顶层对象属性。