以往,在 JavaScript
中声明变量的唯一方式是使用关键字 var
。为了理解为何添加了 let
和 const
,我们先看一个示例,了解使用 var
会带来怎样的麻烦。
var 变量提升
下面代码中你认为运行 getClothing(false)
后的输出是什么?
function getClothing(isCold) {
if (isCold) {
var freezing = 'Grab a jacket!';
} else {
var hot = 'It's a shorts kind of day.';
console.log(freezing);
}
}
答案是输出undifind
, 本质上,在执行任何 JavaScript
代码之前,所有变量都会被“提升”,也就是提升到函数作用域的顶部。因此在运行时,getClothing()
函数实际上看起来如下所示…
function getClothing(isCold) {
var freezing, hot;
if (isCold) {
freezing = 'Grab a jacket!';
} else {
hot = 'It's a shorts kind of day.';
console.log(freezing);
}
}
let 和 const
使用 let
和 const
声明的变量解决了这种提升问题,因为它们的作用域是到块,而不是函数。之前,当你使用 var
时,变量要么为全局作用域,要么为本地作用域,也就是整个函数作用域。
如果在代码块(用花括号 { }
表示)中使用 let
或 const
声明变量,那么该变量会陷入暂时性死区(temporal dead zone
),直到该变量的声明被处理。这种行为会阻止变量被访问,除非它们被声明了。
下面例子中, 你认为运行 getClothing(false)
后的输出是什么?
function getClothing(isCold) {
if (isCold) {
const freezing = 'Grab a jacket!';
} else {
const hot = 'It's a shorts kind of day.';
console.log(freezing);
}
}
答案是 ReferenceError: freezing is not defined.
关于使用 let 和 const 的规则
let 和 const 还有一些其他有趣特性。
- 使用
let
声明的变量可以重新赋值,但是不能在同一作用域内重新声明。 - 使用
const
声明的变量必须赋初始值,但是不能在同一作用域内重新声明,也无法重新赋值。
let instructor = 'James';
instructor = 'Richard';
console.log(instructor); //Richard
const instructor = 'James';
instructor = 'Richard';
console.log(instructor); //SyntaxError: Identifier 'instructor' has already been declared
使用案例
最大的问题是何时应该使用 let
和 const
?一般法则如下:
- 当你打算为变量重新赋值时,使用
let
- 当你不打算为变量重新赋值时,使用
const
。
因为 const
是声明变量最严格的方式,我们建议始终使用 const
声明变量,因为这样代码更容易读懂,你知道标识符在程序的整个生命周期内都不会改变。如果你发现你需要更新变量或更改变量,则回去将其从 const
切换成 let
。
const
实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const
只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
上面代码中,常量foo
储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo
指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。
顶层对象的属性
顶层对象,在浏览器环境指的是window
对象,在 Node
指的是global
对象。ES5
之中,顶层对象的属性与全局变量是等价的。
从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1
let b = 1;
window.b // undefined
上面代码中,全局变量a
由var
命令声明,所以它是顶层对象的属性;全局变量b
由let
命令声明,所以它不是顶层对象的属性,返回undefined
。
var 该怎么办?
还有必要使用 var
吗?没有了。
在某些情况下有必要使用 var
,例如如果你想全局定义变量,但是这种做法通常都不合理,应该避免。从现在开始,建议放弃使用 var
,改为使用 let
和 const
。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。