目前在重看阮一峰老师的 ECMAScript 6 入门),打算将看到的知识点总结归纳。
不论在面试的过程中还是在对代码进行 lint 的过程中总过看到关于 let & const & var 的一些声明的异同的问题,现在就借助阮一峰老师的书籍对这些知识进行总结归纳。
变量提升
let
命令所声明的变量一定要在声明后使用,否则报错。
官方的说法是在 let 声明一个变量之前使用它是会发生错误。官方没有说到会发生变量提升(hoisting),但是有这样的两种现象,在 let 声明一个变量之前使用这个变量,会提示Uncaught ReferenceError: Cannot access 'foo' before initialization
(不应在变量初始化之前使用这个变量)。而如果使用一个未声明的变量,则会提示Uncaught ReferenceError: bar is not defined
console.log(foo);
let foo = 1;
// Uncaught ReferenceError: Cannot access 'foo' before initialization
console.log(bar)
// Uncaught ReferenceError: bar is not defined
Let & const 上述的与 var 声明不同之处带来的另一个不同就是:暂时性死区,
但是说到暂时性死区,就不得不先提一下块级作用域。
块级作用域
在 ES5 中只存在全局作用于和函数作用域(指在函数内声明的所有变量在函数体内始终是可见的,可以在整个函数的范围内使用及复用)。函数作用域会导致一些不合理的地方,这也是面试过程中会考的一些地方,比如在阮老师的博客中的例子:
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}
f(); // undefined
而 ES6 新增的块级作用域可以很好的解决这个问题,因为在块级作用域 ({})
声明的变量是在块级作用域的外部取不到的。
而函数在块级作用域内的声明遵循下列规则(可以遵守):
- 允许在块级作用域内声明函数。
- 函数声明类似于
var
,即会提升到全局作用域或函数作用域的头部。 - 同时,函数声明还会提升到所在的块级作用域的头部。
同样是阮老师的例子:
// 浏览器的 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
而这些又跟暂时性死区又有什么关系呢?
暂时性死区(tdz)
上述所说,块级作用域内声明的变量在此之作用域之外是无法取到的。同样的在内部,
ES6 明确规定,如果区块中存在
let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
这样的情况就可以称之为暂时性死区(TDZ)。
也就是我们最开始说的在使用 let 关键字声明变量之前声明一个变量会报错:
Uncaught ReferenceError: Cannot access 'aa' before initialization
除了这些方面,const 还有其独特的不同之处:
const 声明
我们也知道,const 定义一个常量,常量的值是不能改变的,也意味着声明常量的同时就要给其赋值。其作用域及其表现和 let 相同,也存在TDZ。
但是虽然说到现在 const 都很符合我们的理解,但是 const 所代表的定义的值不能被改变是指变量声明的时候指向的内存地址所保存的数据不得改动[1]。对于基础类型的值,const 声明的变量的值就是内存地址所保存的数据。而对于引用类型的话,const 声明的变量的值就是指向值所在的内存地址的一个指针。const 不能修改的也是这个指针,而指针所代表的内存地址的值可以发生改变。
总结
let const var 三个关键字声明变量的不同之处有:
- var 声明的变量存在变量提升,let & const 声明的变量,在声明之前使用会报错,而 const 声明必须在声明的同时为其赋值;
- ES6 新增了块级作用域,变量以及函数声明在块级作用域内的表现与在函数作用域内的声明不同,但是一些浏览器可以有自己的规则,保证老代码的运行正确;
- const 声明的变量如果是基础类型,不能被修改。如果是引用类型,则可以修改引用类型的属性。
from ECMAScript 6 入门 阮一峰 ↩︎