【发布时间】:2016-01-02 09:27:57
【问题描述】:
ECMAScript 6 中的块作用域函数是什么?
谁能帮我了解块作用域功能与 ECMAScript 5 相比的主要区别?
【问题讨论】:
-
如果您询问的不是块作用域中的函数,而是一般块作用域,请参阅Are variables declared with let or const not hoisted in ES6?
标签: javascript
ECMAScript 6 中的块作用域函数是什么?
谁能帮我了解块作用域功能与 ECMAScript 5 相比的主要区别?
【问题讨论】:
标签: javascript
与古老的var相比,ES2015(又名“ES6”)中新的let和const有四个主要区别:
它们有块作用域
它们没有被吊起(嗯,它们是某种被吊起的,但以一种有用的方式)
重复声明是错误
在全局范围内使用时,它们不会创建全局对象的属性(尽管创建了全局变量;这是 ES2015 的新概念)
(对于它的价值,这在我最近的书 JavaScript: The New Toys 的第 2 章中有详细介绍,其中涵盖了 ES2015-ES2020。)
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 块之外;以上三个as 都是同一个变量。
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 外...因为for 循环外部的a 与for 循环内部的a 不同。
这是有效的代码:
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;
}
在声明之前,您不能使用该变量。声明没有“提升”(嗯,它是部分提升的,请继续阅读)。
我之前说过
- 它们没有被吊起(嗯,它们有点被吊起,但以一种有用的方式)
“有点”?是的。 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"。为什么?因为for 块中的let a 遮蔽了块外的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 指的是全局对象,而在浏览器中,有一个全局对象指的是全局对象: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(它提供了一种新的、更简洁的语法来创建构造函数和与之关联的原型对象)与函数声明(而不是函数表达式):
class 声明具有块范围。相反,在流控制块中使用函数声明是无效。 (这应该是语法错误;相反,不同的 JavaScript 引擎处理它的方式不同。有些将其重新定位到流控制块之外,有些则表现得好像您使用了函数 expression 一样。)李>
class 声明没有被提升;函数声明是。class 声明是语法错误;使用函数声明,第二个获胜,覆盖第一个。class 全局范围内的声明不会创建全局对象的属性;函数声明。提醒一下,这是一个函数声明:
function Foo() {
}
这些都是函数表达式(匿名):
var Foo = function() {
};
doSomething(function() { /* ... */ });
这些都是函数表达式(命名):
var Foo = function Foo() {
};
doSomething(function Foo() { /* ... */ });
【讨论】: