
1.1 let及const
Let及const命令是ES6新增的两种新的声明格式,用于补全ES5标准中var声明变量的不足,下面具体介绍这两种命令。
1.1.1 let命令
在JS中是通过关键字“var”来声明变量的,但是在JS中用“var”来声明变量会出现变量提升的情况,代码如下:

这段代码中,如果没有声明var a=10的话,打印变量a会出现“a is not defined”的错误,但是用“var”声明变量“a”后,“a”的打印结果是undefined,出现这种结果的原因是因为“var”声明变量时的提升机制(Hoisting)导致的。实际上,在执行过程中JS会把上面代码解析成如下格式:

也就是说通过“var”声明的变量系统都会把声明隐式地升至顶部,这样的特性往往会让刚接触JavaScript及习惯其他语言的开发人员不适应,导致程序出现问题。所以针对以上情况,ES6引入了let命令来声明变量。let声明和var声明用法一致,但是不会出现变量突然提升的情况,具体代码如下:

利用let声明还可以把变量的作用域限制在代码块中,ES5中定义作用域有两种,全局作用域和函数作用域。ES5中没有块级作用域的概念,因此ES6中新增了块级作用域,用{}表示。块级作用域用于声明作用域之外无法访问的变量。主要有两种:
1)函数内部块级作用域:

2)在字符{}之间的区域:

let在使用过程中除了上述情况外,还需要注意let声明过程中是禁止重复声明的:

1.1.2 const命令
ES6中还提供了const关键字。使用const声明的是常量,常量的值不能通过重新赋值来改变,并且不能重新声明,所以每次通过const来声明的常量必须进行初始化。

与其他语言不同,const在使用过程中如果声明的是对象,需要注意修改对象的属性值,但是不允许修改已经声明的对象。例如:

如果想让对象属性不能修改,可以借助Object.freeze函数来冻结对象,实现代码如下:

但是通过Object.freeze冻结对象需要注意不能冻结多层对象:

要解决多层对象的冻结问题可以通过封装一个deepFreeze函数来实现:

1.1.3 临时死区
let与const都是块级标识符,所以let与const都是在当前代码块内有效,常量不存在变量提升的情况。但是通过let及const声明的常量,会放在临时死区(temporal dead zone),通过下面代码可以看出:

即使通过安全的typeof操作符也会报错,原因是JavaScript引擎在扫描代码变量时,要么会把变量提升至顶部,要么会把变量放在临时死区。这里通过let来声明“a”变量,会把“a”变量放在临时死区,所以在声明之前打印就会报错。
1.1.4 循环中的let及const
在ES5标准中,for循环都是通过var来声明的,由于var没有独立的作用域,导致在循环中创建函数时会出现结果和思路不一致的情况。代码如下:

循环执行结果并不是预想的0,1,2,3,4而是5个5,这是因为var声明在循环中作用域共用,并且会把i保存在全局作用域中。要解决循环中保存函数的问题,可以利用闭包创建独立作用域。将代码改写如下:

这样通过自执行函数就可以解决循环中创建函数的问题。但是利用ES6中let及const提供的块级作用域可以让上面写法变得更加简单。代码如下:

这样得到的结果就是预想的结果。由于const不能被重新赋值,所以在for循环中如果利用const来定义变量会报错。代码如下:

在for-in或for-of循环中使用const时,方法与let一致,代码如下:
