'let' 是否覆盖全局声明并抛出 ReferenceError?

Does 'let' override a global declaration and throws a ReferenceError?

我正在查看 varlet 文档示例之间的差异,并测试当调用未声明的变量时,全局范围会自动为其提供声明(这就是为什么以下内容片段不会在任何变量中引发错误):

x = 3;
console.log(x);

(function() {
  y=x+39;
})()
console.log(y);

然而,当一个变量在同一全局范围内赋值后用let声明时:

x=3;
let x = 42;
console.log(x);

抛出以下错误之一:

ReferenceError: x is not defined (Chromium)

ReferenceError: can't access lexical declaration x before initialization (Firefox)

我知道 let 不允许 x 提升,但由于它之前被引用(暗示从全局范围自动声明)在这种情况下不应该重新声明发生了什么?

SyntaxError: Identifierx has already been declared

因此抛出了上面的错误?

我也明白在严格模式中第一个片段会抛出一个ReferenceError,所以这是否意味着let在全局范围内强制执行此严格模式的特定规则(所有变量都需要声明)?

您是否看过 MDN 上的 let 文档?他们用 let 描述了一个 时间死区和错误。

ES6 确实将 let 变量提升到其作用域的顶部。与 var 变量不同,当使用 let 时,您不能在变量声明之前访问它。这样做失败并出现 ReferenceError(a.k.a。 让我们暂时死区 )。

正如 Konstantin A. Magg 所解释的,那是因为 let 变量被提升并尝试在初始化抛出(时间死区)之前引用它们。

如果你不想这样,你可以将代码拆分成不同的脚本:

<script>
x = 3;
console.log(x); // 3
</script>

<script>
let x = 42;
console.log(x); // 42
</script>

注意 x = 3 将在严格模式下抛出。

你说得对,这是一种奇怪的行为。它给出这些错误的原因是因为它认为您正在尝试将值 3 分配给您的 let 变量而不是全局值。正如其他人提到的,这会导致提升的时间死区问题。

The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable’s LexicalBinding is evaluated

- Source (ECMAScript 8th edition)

此代码显示放置代码导致 TDZ 的位置:

// Accessing `x` here before control flow evaluates the `let x` statement
// would throw a ReferenceError due to TDZ.
// console.log(x);

let x = 42;
// From here on, accessing `x` is perfectly fine!
console.log(x);

您可以看到将 let 包裹在它自己的块中可以修复它:

x=3;
{
let x = 42;
console.log(x); // 42
}

或者,您可以在 window 对象上显式定义全局:

window.x=3;

let x = 42;
console.log(x);  // 42