此示例摘自mdn,并且由于
而引发错误n.a的指令let n已在的私有范围内 for循环的块,因此将标识符“ n.a”解析为 位于对象第一部分的'n'对象的属性'a' 指令本身(“ let n”),该指令仍处于时间盲区 因为它的声明语句尚未到达并终止。
function go(n) {
for (let n of n.a) {
console.log(n);
}
}
go({
a: [1, 2, 3]
});
现在,我将变量名从n
更改为k
,它可以工作了。但是根据mdn
,let k
仍将处于时间盲区。 那怎么不抛出错误而是记录值呢?
function go(n) {
for (let k of n.a) {
console.log(k);
}
}
go({
a: [1, 2, 3]
});
答案 0 :(得分:1)
但是根据mdn
let k
仍将处于暂时的盲区。
否,到您在代码(console.log(k)
)中使用它时,它已经使用该循环的迭代值进行了初始化。它不在TDZ中。
let n
版本的TDZ问题在这里:
for (let n of n.a) {
// -----------^
那时,用n
声明的let
遮盖了n
参数。它是保留的,并且在TDZ中,但是代码正在尝试在n
中使用它(而不是of n.a
参数)。对于您的k
版本来说,这不是问题,因为您在那里没有引用k
。
答案 1 :(得分:1)
for (let n of n.a) {
// ^^^^^^
该语句的时间盲区是带下划线的of n.a
部分。由于它包含对n
(由let n
声明的变量)的引用,因此是错误的。
for (let k of n.a) {
// ^^^^^^
在此示例中,时间盲区仍然由n.a
组成,但是我们现在声明一个不同的变量k
。由于死区中的表达式不使用k
,所以很好。
答案 2 :(得分:1)
在第一种情况下,这是一个ReferenceError,因为如果在of
的两边都使用了变量名,则规范希望避免混淆。从理论上讲,即使第一个版本也可以工作,然后这样做:
for(let n of n.a) { /*body*/ }
可以执行为:
const iterator = n.a[Symbol.iterator](); // the iterator is initialized in the parent scope
let done = true;
while(!done) {
let n;
({ value: n, done } = iterator.next()); // "n" is initialized in the local loop scope.
/* body */
}
但是,如果右侧的n.a
不引用左侧的n
,那会造成混乱,因此规范的作者添加了另一个作用域,以防止变量循环在迭代器中使用:所有局部变量也都在另一个作用域中声明,该作用域仅用于初始化迭代器:
{ // another local scope (named TDZ in the spec)
const iterator = n.a[Symbol.iterator](); // "n" cannot be used here, as it was not yet initialized
let done = false;
let n; // this is just to prevent "n" from being used in the line above
while(!done) {
let n;
({ value: n, done } = iterator.next());
}
}
规范的相关部分:
13.7.5.12运行时语义:ForIn / OfHeadEvaluation
[...]
b. Let TDZ be NewDeclarativeEnvironment(oldEnv). [...] d. For each string name in TDZnames, do i. Perform ! TDZEnvRec.CreateMutableBinding(name, false). e. Set the running execution context's LexicalEnvironment to TDZ. 3. Let exprRef be the result of evaluating expr
在这种情况下, expr 是n.a
,而 TDZnames 包含n
。
答案 3 :(得分:0)
“ n在TDZ中”是指循环声明中的第二个n,“ let n of n .a ”。使用“ for(让k为n.a)” 不存在此问题:引用 k 的唯一时间是循环内。