我的JavaScript书籍“JavaScript The Definitive Guide,第6版”,第270页包含以下文本和代码:
“...在for循环中,初始化表达式的计算超出了新变量的范围”
let x = 1;
for (let x = x + 1; x < 5; x++) {
console.log(x); // prints 2, 3, 4
}
当我运行上面的代码(在最新版本的Chrome和FF中)时,我收到控制台错误:
ReferenceError:未定义x
初始化之前无法访问词法声明`x'
书中的代码是否不正确? (本书的勘误表网站上没有任何内容:这个。)
唯一的问题是x
正在
重新声明
阴影(如上面的Jonas所述),因此它会抛出一个错误。
只需删除第二个let
,一切都会按预期工作。
let x = 1;
for (x = x + 1; x < 5; x++) {
//^---- note the missing "let" here.
console.log(x); // prints 2, 3, 4
}
如果你从书中复制了那本书,那就是书籍问题。
https://jsfiddle.net/hto9udmj/
关于变量声明的更多信息可以在这里找到:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
关于变量阴影的更多信息:An example of variable shadowing in javascript
问题不在于x
被宣布两次。只是你在初始化之前尝试访问内部x
:
let x = x /* doesn't exist yet*/;
另外在外部范围内有另一个x
(for
循环中的初始化器在它们自己的范围内)并不重要,x
将引用当前范围中的变量,因为它已经声明(由于提升),但是尚未初始化:
let x = 0; // irrelevant
{ // x gets declared as part of this scope
x; // thats an error too as x is not initialized yet
let x = 1; // initialization
x; // now it can be accessed
}
范围开头和let
声明之间的部分称为“时间死区”......
“...在for循环中,初始化表达式的计算超出了新变量的范围”
不,否则您无法在初始化程序中引用其他变量:
for(let a = 1, b = a; ; )
一如既往,明确的答案可以在规范中找到:
13.7.4.7运行时语义:LabelledEvaluation
IterationStatement:for(LexicalDeclaration Expression; Expression)Statement
- 让oldEnv成为正在运行的执行上下文的LexicalEnvironment。
- 设loopEnv为NewDeclarativeEnvironment(oldEnv)。
[...]
- 让boundNames成为LexicalDeclaration的BoundNames。
- 对于boundNames [..]的每个元素dn 表演! loopEnvRec.CreateImmutableBinding(dn,true)。
- 将正在运行的执行上下文的LexicalEnvironment设置为loopEnv。
- 让forDcl成为评估LexicalDeclaration的结果。
[...]
如您所见,正在运行的执行上下文是loopEnv
,而LexicalDeclaration(初始化程序)被评估,而不是oldEnv
。
TLDR:这个例子不仅错误,而且还有段落。
您正在初始化x两次,从而出错。将一个x重命名为i
let x = 1;
for (let i = x + 1; i < 5; i++) {
console.log(i); // prints 2, 3, 4
}
书中的代码是否不正确? (本书的勘误表网站上没有任何内容:这个。)
我相信这些书是正确的;当let
几年前第一次在Firefox中推出时。
具体来说,它没有temporal dead zone,它的内部行为更像var
,只是块范围。
在Firefox 44中,有一个突破性的变化使let
和const
遵循标准:
https://blog.mozilla.org/addons/2015/10/14/breaking-changes-let-const-firefox-nightly-44/
包括引入时间死区。
所以,是的,这本书现在是不正确的;因为你正在尝试做类似的事情:
let x = 0;
{
let y = x; // `let` is block-scope,
// so this `x` is actually the `x`
// defined below, not the one outside
// the scope, hence the `ReferenceError`.
let x = 1;
}
问题是你在x
循环中重新声明for
,因为let
只存在于给定的上下文中,循环完成后x
不再存在。
要么在x
循环之外声明for
,要么使用var
。 var
将变量添加到全局范围,因此它将在for
循环完成后存在。
let x = 1;
for (x = x + 1; x < 5; x++) {}
console.log(x);
随着var:
for (var x = 2; x < 5; x++) {}
console.log(x);