1.用于声明变量。用法类似于var,但是let声明的变量只在let命令所在的代码块内有效;
2.不存在变量提升。
var命令回出现“变量提升”现象,即变量可以在声明之前使用,值为undefined。
let声明的变量一定要在声明后使用,否则会报错。
3.暂时性死区。
只要块级作用域内存在let
命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
上面代码中,存在全局变量tmp
,但是块级作用域内let
又声明了一个局部变量tmp
,导致后者绑定这个块级作用域,所以在let
声明变量前,对tmp
赋值会报错。
如果区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成封闭作用域。只要在声明前使用这些变量,就会报错;“暂时性死区”(temporal dead zone,简称TDZ);
4.不允许重复声明
1.没有块级作用域,会导致很多场景不合理:
2.ES6的块级作用域:
3.块级作用域与函数声明:
// 情况一
if (true) {
function f() {}
}
// 情况二
try {
function f() {}
} catch(e) {
// ...
}
let
,在块级作用域之外不可引用。// 第一部分
function f() { console.log('I am outside!'); }
(function () {
if (false) {
// 重复声明一次函数f
function f() { console.log('I am inside!'); }
}
f();
}());
// 第二部分---ES5 环境
function f() { console.log('I am outside!'); }
(function () {
function f() { console.log('I am inside!'); }
if (false) {
}
f();
}());
注意:
if
内声明的函数f
会被提升到函数头部,实际执行的是第二部分的代码。let
,对作用域之外没有影响。但是,如果你真的在 ES6 浏览器中运行一下上面的代码,是会报错的。原因是:如果改变了块级作用域内声明的函数的处理规则,显然会对老代码产生很大影响。为了减轻因此产生的不兼容问题,ES6 规定,浏览器的实现可以不遵守上面的规定,有自己的行为方式。
-----允许在块级作用域内声明函数。
-----函数声明类似于var
,即会提升到全局作用域或函数作用域的头部。
-----同时,函数声明还会提升到所在的块级作用域的头部。
上面三条规则只对 ES6 的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域的函数声明当作let
处理。
根据这三条规则,在浏览器的 ES6 环境中,块级作用域内声明的函数,行为类似于var
声明的变量。
// 第一部分----浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {
if (false) {
// 重复声明一次函数f
function f() { console.log('I am inside!'); }
}
f();
}());
// Uncaught TypeError: f is not a function
// 第二部分----浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {
var f = undefined;
if (false) {
function f() { console.log('I am inside!'); }
}
f();
}());
// Uncaught TypeError: f is not a function
1.声明常量,一旦声明,就必须立即初始化,不能留到以后赋值且不可改变;
2.只在声明所在的块级作用域内有效;
3.声明的常量不提升,存在暂时性死区,只能在声明后使用;
4.const
声明的常量,也与let
一样不可重复声明。
注意:
const
实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动;const
只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
上面代码中,常量foo
储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo
指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。
ES5 只有两种声明变量的方法:var
命令和function
命令。ES6 除了添加let
和const
命令,后面章节还会提到,另外两种声明变量的方法:import
命令和class
命令。所以,ES6 一共有 6 种声明变量的方法。