1. let 命令
1.1 基本用法
ES6 新增了 let 命令,用来声明变量。它的用法类似于 var 但是所声明的变量,只在 let 命令所在的代码块内有效。
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined
b // 1
let 声明的变量报错,var没有,这表明,let 声明的变量只在它所在的代码块有效。
for 循环的计数器,适合用 let 命令
for( let i=0; i<10;i++){
// ...
}
console.log(i) // ReferenceError: a is not defined
i 只在 for 内有效,循环体外引用就报错
下面的代码如果是用 var,最后输出的是 10
var a = []
for(var i=0;i<10;i++){
a[i] = function(){
console.log(i);
};
}
a[6](); // 10
上面代码中,变量 i 是 var 命令声明的。在全局内有效,所以全局内只有一个变量 i 。
每一次循环,变量 i 的值都会发生改变,而循环内被赋给数组 a 的函数内部的 console.log(i)里的i指向全局的i。也就是说,所有数组 a 成员里面的i,指向的都是同一个 i 导致运行时输出的是最后一轮的 i 值(10)。
如果使用 let 声明的变量仅在块级作用域内有效,最后输出的是6.
var a = [];
for(let i=0;i<10;i++){
a[i] = function(){
console.log(i)
}
}
a[6]() // 6
上面代码中,变量 i 是let 声明的,当前的 i 只在本轮循环有效,每次循环 i 都是一个新的变量,所以最后输出的是 6.你可能会问,如果每一轮循环的变量 i 都是重新发明的,那它怎么知道上一轮循环的值,从而计算出本循环的值?这时因为 JavaScript 引擎内部记住上一轮循环的值,初始化本轮的变量 i 时,就在上一轮循环的基础上进行计算。
另外,for 循环还有一个特别之处。就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
for(let i=0;i<3;i++){
let i = 'abc'
console.log(i)
}
// abc
// abc
// abc
正确运行,输出3次abc。表明函数内部的变量 i 与循环变量 i 不在同一个作用域,有各自单独的作用域
1.2 不存在变量提升
var 命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。
let 命令改变了语法行为,它所生命的变量一定要在声明后使用。否则报错
// var
console.log(foo) // undefined
var foo = 2;
// let
console.log(bar) // ReferenceError
let bar = 2;
1.3 暂时性死区
只要块级作用域内存在 let 命令,它所生命的变量就绑定这个区域。
var tmp = 123;
if(true){
tmp = 'abc' // ReferenceError
let tmp;
}
存在全局变量tmp,但是块级作用域内let 又声明了一个局部变量tmp。导致后者绑定了这个块级作用域,所有在let声明变量前,对tmp赋值会报错。
ES6 明确规定。如果区块内存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
总之,在代码块内 使用 let 命令声明变量之前,该变量都是不可用的。这在语法上称为“暂时性死区”(temporal dead zone,简称 TDZ)
if(true){
// TDZ 开始
tmp = 'abc' //ReferemceErrpr
let tmp; // TDZ 结束
console.log(tmp) // undefined
tmp = 123
console.log(tmp) // 123
}
// let 命令声明变量 tmp 之前 都属于 变量 tmp 的死区
“暂时性死区”,也意味着 typeof 不再是一个百分之百安全的操作。
typeof x // ReferenceErrpr
let x;
变量 x 使用 let 命令声明,所以在声明之前,都属于 x 的死区,只要用到该变量就会报错。
因此 typeof 运行时就会抛出一个 ReferenceError
**
作为比较 如果一个变量根本没有被声明,使用typeof 反而不会报错。
typeof undeclared_variable // undefined
有些死区比较隐蔽,不太容易发现
function bar( x=y,y=2){
return [x,y];
}
bar() // 报错
// x = y ,因为 y 还没有声明,属于“死区”,如果y的默认值是就不会报错/
下面的代码也会报错, 与 var 的行为不同。
var x = x // 不报错
let x = x // ReferemceErrpr: x is not defiend
因为暂时性死区。使用let声明变量时,只要变量在还没有声明完成前使用,就会报错,上面这行就属于这个情况,在变量 x 的声明语句还没有执行完成前就去取 x 的值,导致报错 “x ” 未定义
ES6 规定暂时性死区和 let 、const语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外i的行为。这样的错误在ES5是很常见的,现在有了这种规定,避免此类错误就很容易了。 总之,暂时性死区的本质就是,只要已进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的哪一行代码出现,才可以获取和使用该辨明
1.4 不允许重复声明
let 不允许在相同作用域内,重复声明同一个变量
// 报错
function func(){
let a = 1;
var a = 1;
}
//
function func(){
let a = 1;
let a = 1;
}
因此,不能在函数内部重新声明参数。
function func(arg){
let arg;
}
func() // 报错
function func(arg){
{
let arg;
}
}
func() // 不报错