外部范围中的已定义变量未在内部范围

时间:2017-01-10 12:59:23

标签: javascript scope ecmascript-6

以下代码抛出ReferenceError“a未定义”。

{
    let a = 'a1';
    {
        console.log(a);
        let a = 'a2';
    }
}

和下一个一样。

{
    const a = 'a1';
    {
        console.log(a);
        const a = 'a2';
    }
}

如果你改为使用var声明,它就像我期望的那样工作。 (没有抛出错误错误并记录'a1'。)

当我尝试分析以下代码时,我更难理解。

{
    let a = 'a1';
    {
        console.log(a);
        var a = 'a2';
    }
}

抛出一个SyntaxError,“标识符'a'已经被声明了。”

Naïvely我希望一个标识符在一个let或const声明之前被遮蔽,比如在Clojure中let的行为或者在Racket中让*。如described clearly on MDN,这不是它的工作原理。

为什么以这种方式工作?为什么Racket既有let又有let *形式?

1 个答案:

答案 0 :(得分:1)

这是因为将内部letconst提升到了区块的顶部(MDN),并创建了时间死区。< / p>

  

在ECMAScript 2015中,让我们将变量提升到顶部   块。但是,在之前引用块中的变量   变量声明导致ReferenceError。变量在a中   从块的开始直到声明的“时间死区”   处理完毕。

这背后的原因很简单 - 无声失败vs投掷错误。

在此示例中,由于undefined提升,类似设置将结果显示为var。这是一个无声的错误,可能很难调试。

{
    var a = 'a1';
    (function() {
        console.log(a);
        var a = 'a2';
    })();
}

如果您使用letconst,则会引发错误:

{
  let a = 'a1';
  (function() {
    console.log(a);
    let a = 'a2';
  })();
}

您可以在文章TEMPORAL DEAD ZONE (TDZ) DEMYSTIFIED中找到有关时间死区的更多信息。