【问题标题】:Does 'let' override a global declaration and throws a ReferenceError?'let' 是否覆盖全局声明并引发 ReferenceError?
【发布时间】:2017-05-17 23:57:11
【问题描述】:

我正在查看 varlet 文档示例之间的差异,并且正在测试当调用未声明的变量时,全局范围会自动为其提供声明(这就是为什么下面的 sn-p 不会抛出任何变量中的错误):

x = 3;
console.log(x);

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

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

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

抛出以下错误之一:

ReferenceErrorx 未定义(Chromium)

ReferenceError:在初始化之前无法访问词法声明x (Firefox)

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

SyntaxError:Identifierx 已被声明

因此抛出了上面的错误?

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

【问题讨论】:

  • let is hoisted as well,您只是在时间死区访问它。我看不出这与严格模式有什么关系。
  • @Bergi 我不完全了解TDZ,我假设一个未声明的变量将在全局范围内自动声明(此时docs 只显示双@ 987654336@ 声明)。由于在其自己的 let 声明之前访问变量会引发 ReferenceError,我认为严格模​​式行为/规则是相关的,但现在我明白这是由于 let 提升变量。

标签: javascript scope ecmascript-6 global let


【解决方案1】:

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

变量是在它们包含的词法环境为 已实例化,但 在变量的 评估 LexicalBinding

-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

【讨论】:

  • 怎么样:` var a = 1;函数 fn() { console.log(a);让 a = 2; } fn(); `
  • @AntonYaskevich 同样,您正在尝试在评估语句之前记录x
【解决方案2】:

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

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

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

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

注意x = 3 将进入严格模式。

【讨论】:

    【解决方案3】:

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

    ES6 确实将let 变量提升到其作用域的顶部。与var 变量不同,当使用let 时,您不得在声明之前访问该变量。这样做会失败并出现ReferenceError(a.k.a.let's temporal dead zone)。

    【讨论】:

    • 但不是 x 初始化为 3 ,它在第一条语句中默认为全局声明。因此,无论 let 是否提升变量,它都应该在语句 #2 处具有引用。你有什么看法?
    • let x; 被提升到范围的顶部——在这种情况下是全局范围。语句x=3; 无法创建新的var,因为在此范围内,该名称已为我们的let 保留。它也无法访问变量x,因为它还没有声明。
    • 所以,这是一种阻止变量在声明前使用的行为。这是有道理的。但是,我必须更多地研究它才能确定。谢谢
    • @KonstantinA.Magg 你在your comment 中的话让我比你回答中的话更能理解它。谢谢。
    猜你喜欢
    • 2019-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-14
    • 1970-01-01
    • 1970-01-01
    • 2021-04-12
    相关资源
    最近更新 更多