基本用法
ES6新增了let
命令,用来声明变量。它的用法类似于var
,但是所声明的变量,只在let
命令所在的代码块内有效。
上面代码在代码块之中,分别用let
和var
声明了两个变量。然后在代码块之外调用这两个变量,结果let
声明的变量报错,var
声明的变量返回了正确的值。这表明,let
声明的变量只在它所在的代码块有效。
for
循环的计数器,就很合适使用let
命令。
上面代码中,变量i
是var
声明的,在全局范围内都有效。所以每一次循环,新的i
值都会覆盖旧值,导致最后输出的是最后一轮的i
的值。
如果使用let
,声明的变量仅在块级作用域内有效,最后输出的是6。
上面代码中,变量i
是let
声明的,当前的i
只在本轮循环有效,所以每一次循环的i
其实都是一个新的变量,所以最后输出的是6。
不存在变量提升
let
不像var
那样会发生“变量提升”现象。所以,变量一定要在声明后使用,否则报错。
上面代码中,变量foo
用var
命令声明,会发生变量提升,即脚本开始运行时,变量foo
已经存在了,但是没有值,所以会输出undefined
。变量bar
用let
命令声明,不会发生变量提升。这表示在声明它之前,变量bar
是不存在的,这时如果用到它,就会抛出一个错误。
暂时性死区
只要块级作用域内存在let
命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
上面代码中,存在全局变量tmp
,但是块级作用域内let
又声明了一个局部变量tmp
,导致后者绑定这个块级作用域,所以在let
声明变量前,对tmp
赋值会报错。
ES6明确规定,如果区块中存在let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称TDZ)。
上面代码中,在let
命令声明变量tmp
之前,都属于变量tmp
的“死区”。
“暂时性死区”也意味着typeof
不再是一个百分之百安全的操作。
上面代码中,变量x
使用let
命令声明,所以在声明之前,都属于x
的“死区”,只要用到该变量就会报错。因此,typeof
运行时就会抛出一个ReferenceError
。
作为比较,如果一个变量根本没有被声明,使用typeof
反而不会报错。
上面代码中,undeclared_variable
是一个不存在的变量名,结果返回“undefined”。所以,在没有let
之前,typeof
运算符是百分之百安全的,永远不会报错。现在这一点不成立了。这样的设计是为了让大家养成良好的编程习惯,变量一定要在声明之后使用,否则就报错。
有些“死区”比较隐蔽,不太容易发现。
上面代码中,调用bar
函数之所以报错(某些实现可能不报错),是因为参数x
默认值等于另一个参数y
,而此时y
还没有声明,属于”死区“。如果y
的默认值是x
,就不会报错,因为此时x
已经声明了。