给出以下简化代码,为什么会出现ReferenceError?
function foo(n){
for (let i = 0; i < n; i++) {
console.log(i);
let i = 10;
}
}
foo(4);
// OUTPUT: ReferenceError: i is not defined
let
变量应与声明的所有其他类型一样悬挂。因此,在此代码中i
被声明两次。首先,在初始化中关闭,然后在for循环中本身。
为什么会输出i is not defined
而不是Identifier 'i' has already been declared
。
JS如何读取上面的代码?
答案 0 :(得分:2)
与
let
变量应与声明的所有其他类型一样悬挂。
let
变量以及函数和类声明不同, var
变量不是 悬挂的。 const
变量是相同的。
直到您到达声明该变量的行,该变量才存在。
但是,在到达声明之前,您无法访问相关范围内的变量。这有点无助,被称为“时间死区”。 (Useful 2ality blog)
这很有用,因为在声明变量之前访问变量几乎总是错误。或者,至少,它会造成混淆,并且在重构时会导致错误。
function foo(n){
for (let i = 0; i < n; i++) {
// i cannot be accessed
let i = 10;
// i is set to 10
}
}
foo(4);
至于为什么它不是“标识符已声明”错误,在for
循环头中声明的变量的范围是与在块中声明的变量分开的变量。这段代码本质上有三个作用域:外部作用域,一个开始于for
循环头和一个开始于{}
包围的块中的作用域。
答案 1 :(得分:1)
在下面发生的事情是JavaScript在同一个for循环中看到了两个不同级别的块作用域。
第一个在循环的初始化子句(let i = 0;
)中声明,第二个在循环本身内部声明。
这是您第一次运行代码时发生的情况:
let
i = 0
;
let
内部 for循环:i is set to undefined
当for循环命中console.log(i)
时,它将在循环内找到let i
的声明,该声明将覆盖先前声明和分配的let i
(在初始化子句中。)
由于每次吊起,let
和const
被声明但未初始化(如undefined
),因此将引发ReferenceError。
答案 2 :(得分:-2)
根据MDN:
function test(){
var foo = 33;
if (true) {
let foo = (foo + 55); // ReferenceError
}
}
test();
由于词法作用域,表达式(foo + 55)中的标识符“ foo”的计算结果为if块的foo,而不是值为33的上覆变量foo。 在这一行中,if块的“ foo”已经在词法环境中创建,但是尚未达到(并终止)其初始化(这是该语句本身的一部分):它仍然处于时间盲区。 / p>
这意味着如果在示例中循环变量为“ read”,(在您的情况下为console.log,则无法将其分配给与“死区”中相同的名称var
MDN参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let(在时间盲区下)