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
声明的全局对象将不属于顶层对象属性。