相同情况下的不同 Uncaught ReferenceError

Different Uncaught ReferenceError for same situations

提问人: 提问时间:11/3/2023 更新时间:11/3/2023 访问量:43

问:

我不明白为什么在这些相同的情况下控制台会返回两个不同的输出

console.log(a);
let a = 1;

控制台显示 A 未定义

和这里

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

控制台显示初始化前无法访问“a” 在这两种情况下,它不应该返回“初始化前无法访问'a'”吗?因为我们使用 let 声明来声明变量,并且在这两种情况下它们都在 TDZ 中?为什么说 a 没有定义?

为什么会这样?在全局范围内提升变量与在块范围内提升变量是否不同?

JavaScript 范围 初始化 提升 uncaught-reference-error

评论

2赞 VLAZ 11/3/2023
您正在 REPL 或浏览器控制台(基本上是 REPL)中对此进行测试,并且您没有像运行 JS 文件一样获得准确的行为。真的就是这样。FF 控制台确实报告了第一个代码块的 TDZ 错误。我想,无论你使用什么,都只是一行一行地运行它。只需按应有的方式运行代码,不要从随机 REPL 如何解释某些代码行中得出结论。
1赞 T.J. Crowder 11/3/2023
^^也就是说:在真实环境中测试代码,而不是在控制台的 REPL 中测试代码。 根据它是什么,控制台 REPL 以有用的调试工具的名义执行一些非标准操作,而不是简单范围的准确表示。
0赞 T.J. Crowder 11/3/2023
“在全局范围内吊装变量与在块范围内吊装变量有什么不同吗?”不是在这种情况下,不:jsfiddle.net/yd0xv6cg(全局范围在某些方面有点特殊,我建议使用模块来避免将代码放在全局范围,但同样,您在这里看到的只是控制台行为。
1赞 Barmar 11/3/2023
请记住,控制台一次执行一行。所以在第一个示例中执行时,它还没有看到 .它不能预言未来,所以它不知道你要宣布它。但是在编译函数时,它可以看到所有内容,并且看到声明是在使用之后。console.log(a)let a = 1;
0赞 T.J. Crowder 11/3/2023
@Barmar - 这是真的,但无论哪种情况都应该是,因为代码正在尝试读取未声明标识符的值(如果您逐行读取)。:-)ReferenceError

答:

1赞 T.J. Crowder #1

这只是您使用的控制台处理您粘贴到其中的内容的方式的结果。不要从中吸取任何教训,并在控制台之外应用它。控制台 REPL 执行的几项操作与代码的实际行为方式略有不同,无论是在全局范围还是在函数范围(或模块范围)上。控制台 REPL 在很多方面都很方便,但在没有仔细检查的情况下,不要推断它们对其他环境的作用。

在您的示例中,控制台评估了每个语句,而没有向前扫描到下一个语句(即使您将行粘贴在一起并在它们之间粘贴换行符也是如此)。因此,首先它进行了评估,并且由于没有声明的标识符,因此它为您提供了标准。然后它评估了你的console.log(a);aa is not definedlet a = 1;

但这并不是在全局范围内实际评估代码的方式。如果您有:

console.log(a);
let a = 1;

...JavaScript 引擎首先扫描所有代码,查找诸如 (and and and declarations) 之类的声明,然后为它们创建绑定(大致是变量)。现代声明 (, , ) 的绑定未初始化;旧式绑定 (, 声明) 被初始化(分别使用或与函数一起使用)。因此,当它去评估分步代码时,它会看到它已声明,但未初始化,并给出错误:let avar afunctionclassletconstclassvarvarfunctionundefinedaCannot access 'a' before initialization

console.log(a);
let a = 1;

当你使用一个函数时,它给了你第二个行为,因为该函数在完成之前不会被评估,并且评估的工作方式与我上面描述的全局评估类似,因此 JavaScript 引擎知道它已声明(但未初始化)并抛出相应的错误。a