双重允许声明:让提升如何在for循环中工作?

时间:2018-06-24 13:23:13

标签: javascript for-loop hoisting

给出以下简化代码,为什么会出现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如何读取上面的代码?

3 个答案:

答案 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

enter image description here

当for循环命中console.log(i)时,它将在循环内找到let i的声明,该声明将覆盖先前声明和分配的let i(在初始化子句中。)

由于每次吊起,letconst被声明但未初始化(如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(在时间盲区下)