与传统let
相比,ES2015中的新const
和var
(又名“ES6”)具有四个主要区别:
他们具有块范围不吊起(嗯,它们有点吊起,但是以一种有用的方式)重复声明是错误的在全局范围内使用时,它们不创建全局对象的属性(尽管创建了全局变量;这是ES2015的新概念)块范围
var
变量存在于声明它们的函数中(或全局声明,如果全局声明),它们并不局限于它们所在的块。因此,此代码有效:
function foo(flag) {
a = 10;
if (flag) {
var a = 20;
}
return a;
}
console.log(foo(false)); // 10
console.log(foo(true)); // 20
不管
a
是否为真且是否存在于
flag
块之外,都定义
if
;上面的所有三个
a
都是相同的变量。
let
(或
const
)不是这样的:
function foo(flag) {
if (flag) {
let a = 10;
}
return a; // ReferenceError: a is not defined
}
console.log(foo(true));
a
仅存在于声明的块中。(对于
for
语句,
()
的
for
中的声明实际上被视为该块的一部分。)因此,在
if
之外,
a
不存在。
let
和
const
声明可以在封闭范围内隐藏声明,例如:
function foo() {
let a = "outer";
for (let a = 0; a < 3; ++a) {
console.log(a);
}
console.log(a);
}
foo();
那输出
0
1
2
outer
...because the a
outside the for
loop isn't the same a
as the one inside the for
loop.
Hoisting
This is valid code:
function foo() {
a = 5;
var a = a * 2;
return a;
}
看上去很奇怪,但是有效(返回10),因为
var
在该函数中的其他任何事情之前完成,所以实际上是:
function foo() {
var a; // <== Hoisted
a = 5;
a = a * 2; // <== Left where it is
return a;
}
let
或
const
并非如此:
function foo() {
a = 5; // <== ReferenceError: a is not defined
let a = a * 2;
return a;
}
您必须先声明变量,然后才能使用该变量。声明不是“悬挂”的(嗯,它是部分悬挂的,请继续阅读)。
我刚才说
- They aren't hoisted (well, they're sort of hoisted, but in a useful way)
“有点”?是。尽管
let
或
const
声明仅在实际发生的地方有效,但它在整个出现的代码块中会遮盖一个标识符。示例帮助:
function foo() {
let a = "outer";
for (let x = 0; x < 3; ++x) {
console.log(a); // ReferenceError: a is not defined
let a = 27;
}
}
请注意,我们得到一个错误,而不是在控制台中获得
"outer"
。为什么?因为
let a
块中的
for
遮盖了该块外的
a
,即使我们尚未使用它。规范将块的开头与
let
之间的空间称为“时间死区”。言语不是每个人的事,所以这是一个图:
重复声明
这是有效的代码:
function foo() {
var a;
// ...many lines later...
var a;
}
仅忽略第二个
var
。
let
(或
const
)不是这样的:
function foo() {
let a;
// ...many lines later...
let a; // <== SyntaxError: Identifier 'a' has already been declared
}
不是全局对象属性的全局变量
JavaScript具有“全局对象”的概念,其中包含各种全局事物作为属性。在松散模式下,位于全局范围内的
this
指的是全局对象,在浏览器上有一个指向全局对象的global:
window
。 (某些其他环境提供了不同的全局变量,例如NodeJS上的
global
。)
在ES2015之前,JavaScript中的所有全局变量都是全局对象的属性。从ES2015开始,使用
var
声明的声明仍然适用,但使用
let
或
const
声明的声明仍然无效。因此,此代码在浏览器上的全局范围内使用
var
显示42:
"use strict";
var a = 42; // Global variable called "a"
console.log(window.a); // Shows 42, because a is a property of the global object
但是此代码显示了属性的
undefined
,因为全局范围内的
let
和
const
不会在全局对象上创建属性:
"use strict";
let a = 42; // Global variable called "a"
console.log(a); // 42 (of course)
console.log(window.a); // undefined, there is no "a" property on the global object
const q = "Life, the Universe, and Everything"; // Global constant
console.log(q); // "Life, the Universe, and Everything" (of course)
console.log(window.q); // undefined, there is no "q" property on the global object
最后说明:如果将新的ES2015
class
(为创建构造函数和与之关联的原型(prototype)对象提供新的更简洁的语法)与函数声明(而不是函数表达式)进行比较,上述大部分内容也适用:
class
声明具有块范围。相反,在流控制块中使用函数声明是无效。 (这应该是一个语法错误;相反,不同的JavaScript引擎对它的处理方式不同。一些将其重新定位到流控制块之外,其他的则好像您使用了函数表达式一样。) 没有悬挂
class
声明;函数声明是。 在同一个作用域中对两个class
声明使用相同的名称是语法错误;通过函数声明,第二个赢,覆盖第一个。 全局范围内的
class
声明不会创建全局对象的属性。函数声明呢。 提醒一下,这是一个函数声明:
function Foo() {
}
这些都是函数表达式(匿名的):
var Foo = function() {
};
doSomething(function() { /* ... */ });
这些都是函数表达式(命名的):
var Foo = function Foo() {
};
doSomething(function Foo() { /* ... */ });