let命令,用于声明变量,用法和var类似,但是所声明的变量只在let命令所在的代码块内有效。
{
let a = 1;
var b = 2;
}
console.log(a);// 报错:ReferenceError: a is not defind.
console.log(b);// 2
在代码块外面调用了let命令声明的a就会报错,而调用var声明的b返回了正常的值。这就说明let声明的变量只在其所在的代码块中有效。
下面是一个关于let和var在for循环中的例子:
使用var声明变量的for循序特殊例子:
var a = [];
for(var i = 0; i < 10; i++){
a[i] = function(){
console.log( i );
}
}
a[6] (); // 10
变量i是var声明的,在全局范围内有效,所以全局只有一个变量,每一次循环i的值都会发生改变,被赋值给数组a的函数内部的console.log(i) 的i指向的是全局的i。也就是说,所有数组a的成员中i都指向的是同一个i,导致输出时输出的是最后一轮的i,也就是10。
使用let声明变量的for循序特殊例子:
var a = [];
for(let i = 0; i < 10; i++){
a[i] = function(){
console.log( i );
}
}
a[6] (); // 6
变量i是let声明的,i只在本轮循环内有效,所以每一次循环的i其实都是一个新的变量。这是因为在Javascript引擎内部会记住上一轮的循环的值,初始化本轮的变量i时,就会在上一轮循环的基础上进行计算。
for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for(let i = 0; i <i 10; i++){
let i = 'abc';
console.log( i );
}
// abc
// abc
// abc
……
上面的结果就是连续输出10次的'abc'。这就表明函数内部的变量i与循环变量i不再同一个作用域。
let不存在变量提升的现象。
使用var命令声明的变量会发生 “变量提升”现象,就是变量可以在声明之前使用,值为undefined。按照一般的逻辑,变量应该是在声明之后才能被使用的。当然,在ES6中,let命令改变了语法行为,它所声明的变量一定要在声明后使用。
如:
// var的情况
console.log(foo);// 输出undefined
var foo = 2;
// let的情况
console.log(fo);// 报错 ReferenceError
let fo = 2;
let暂时性死区
只要在块级作用域存在let命令,则它所声明的变量就 “绑定”(binding) 这个区域,不再受外部的影响。
如:
var temp = 123;
if(true){
temp = 'abc'; // ReferenceError
let temp;
}
上面代码中存在全局变量temp,但是在块级作用域内let又声明了一个局部变量temp,导致后者绑定这个块级作用域,所以在let声明变量之前对temp赋值会报错。
在ES6中,如果区域块中存在let和const命令,则这个区域块对这些命令声明的变量从一开始就会形成封闭作用域。只要是在声明之前使用这些变量,则就会报错。
所以在代码块内,使用let命令声明变量之前,该变量都是不可用的,这在语法上称为“暂时性死区”(temporal dead zone,简称TDZ)。
如:
if(true){
// TDZ 开始
temp = 'abc'; // ReferenceError
console.log(temp); //ReferenceError
let temp; // TDZ 结束
console.log(temp); // undefined
temp = 123;
console.log(temp); // 123
}
上面代码中,在let命令声明变量temp之前都是变量temp的“死区”。
这里要提到的一点是,“暂时性死区”也意味着typeof不再是一个百分百安全的操作了。
typeof a;// ReferenceError
let a;
上面代码变量a使用let声明,在声明之前都属于a的“死区”,只要该变量被使用就会报错。
但是,如果一个变量根本就没有被声明,使用typeof反而不会报错。
typeof okey; // undefined
上面代码中okey是一个不存在的变量,所以结果返回undefined。在没有let之前,typeof运算符是百分之百的安全,永远不会报错。
这样的设计初衷也是为了让大家养成良好的编程习惯,变量一定要在声明之后使用,否则就会报错。
在我们平时的实际使用中,还有一些比较隐蔽的“死区”,这样的“死区”不太容易被发现。
如:
function go (x = y, y = 6) {
return [x, y];
}
go(); // 报错
上面代码中,调用go函数之所以报错是因为参数x值等于另外一个参数y,而此时y还没有被声明,属于“死区”。反过来如果y的默认值是x的话,则就不会报错,因为此时x已经被声明了。
如:
function go (x = 6, y = x) {
return [x, y];
}
go(); // [6, 6]
另外,下面的代码也会报错。
如:
// 不报错
var x = x;
// 报错
let y = y; // ReferenceError: y is not defined
上面代码报错也是暂时性死区。
在使用let声明变量时,只要变量在还没有声明前使用就会报错。ES6规定暂时性死区和let、const语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。
暂时性死区的本质就是,只要进入当前作用域,所要使用的变量就已经存在,但是不可获取,只有等到声明变量的那一行代码出现后,才可以获取和使用该变量。
let不允许重复声明
let不允许在相同的作用域内重复声明同一个变量。
如:
// 报错
function () {
let a = 1;
var a = 10;
}
// 报错
function () {
let a = 1;
let a = 10;
}
function func(arg) {
let arg;// 报错
}
function (arg) {
{
let arg; // 不报错
}
}