Chrome 控制台已声明变量为 let 抛出未定义的引用错误

IT技术 javascript scope ecmascript-6 google-chrome-devtools let
2021-02-22 07:06:28

最近我在 chrome 控制台中遇到了这个奇怪的事情。在这里,我故意将一个未定义的东西分配给 a 以抛出错误。

let a = werwr // Uncaught ReferenceError: werwr is not defined

然后,当我尝试将合法的东西分配给 a 时,发生了以下情况:

let a = "legit string"   // Uncaught SyntaxError: Identifier 'a' has already been declared

所以我不能使用“let”,因为 a 已经被声明了。所以我试图将其他东西重新分配给“已经声明的”

a = "legit string"   //  Uncaught ReferenceError: a is not defined

所以似乎我不能将其他东西重新分配给 a 但同时, a 已被声明,所以我不能再次使用 let 。

我理解声明和分配变量之间的区别。然而,在这里似乎两者都不能再做。这与控制台中“让”的范围有关吗?因为同样的事情完全适用于“var”

var a = werwr 
// Uncaught ReferenceError: werwr is not defined

a = ”legit string“  
// ”legit string“

var a = "legit string" 
// Uncaught SyntaxError: Identifier 'a' has already been declared

跟进

“手动”提升 let 语句与隐式案例之间似乎存在一些差异。

throw new Error
let example = 5
// same errors as before

而在这种情况下,示例可以再次重新分配。

let example
throw new Error
example = 5
2个回答

当您将时间死区引入全局范围时,就会发生这种情况。您可能知道,let声明被提升但未初始化由于控制流,可能会发生变量从未初始化的情况:

function …() {
    if (false)
        example; // would throw a ReferenceError if it was evaluated
    … // do something
    if (true)
        return; // stop!
    let example = 5; // never executed
}

这在函数范围内很好。也许出了问题,也许根本不需要该变量 - 在下一次调用中,将创建一个带有新变量的新作用域。

类似的事情可能发生在全局范围内,当您在变量初始化之前抛出异常时(这里只有异常作为控制流构造起作用,没有其他方法可以达到相同的效果)。

throw new Error;
let example = 5;

与函数作用域相反,这里的变量保持未初始化确实很重要。全局作用域永远存在,变量永远死亡它不是也永远不会被初始化,并且词法变量不能被重新声明(这有助于防止错误)。

在 es-discuss 上进行了讨论,但被认为无关紧要。如果顶级<script>执行抛出错误,那么您会遇到比未初始化变量更大的问题。没有办法恢复。如果您需要一个(例如通过尝试在连续的脚本中重新声明它),您var无论如何都必须使用

在 devtools 控制台中遇到同样的问题有点麻烦,但可以将控制台作为特殊范围来解决。

回复“无路可退”,未来或许可以 <script onerror
2021-04-21 07:06:28
考虑到let被提升了,我写了这样的代码:“ let x; throw new Error; x = 6” 这种情况下,x实际上可以稍后重新赋值。“手动”提升声明语句与隐式实现之间有什么细微的区别吗?我以更好的格式添加了对问题的跟进
2021-05-02 07:06:28
是的,let x;是 的快捷方式let x = undefined;,如果您手动将其放在顶部,您将在抛出异常之前初始化变量。
2021-05-02 07:06:28
@Pacerier 您可以处理错误(今天已经使用window.onerror)并记录它,但您无法恢复它。在抛出异常之前无法撤消脚本所做的事情,并且不可能取消声明变量。
2021-05-07 07:06:28

您应该了解 JS 中的提升。基本上,像这样的声明 let a = werwr;

被解释为 let a; a = werwr;

这就是为什么在您运行第二行代码时已经声明了 a 的原因。

更新

所以,ES 规范中有一个注释 https://tc39.github.io/ecma262/#prod-LetOrConst

let 和 const 声明定义了作用域为运行执行上下文的 LexicalEnvironment 的变量。变量在其包含的 Lexical Environment 被实例化时创建,但在评估变量的 LexicalBinding 之前不能以任何方式访问当 LexicalBinding 被求值时,一个由 LexicalBinding 和 Initializer 定义的变量被分配了它的 Initializer 的 AssignmentExpression 的值,而不是在创建变量时。如果 let 声明中的 LexicalBinding 没有初始值设定项,则在评估 LexicalBinding 时会为变量分配未定义的值。

...

“但在评估变量的 LexicalBinding 之前不能以任何方式访问” 意味着,必须成功完成声明才能访问变量(获取值、赋值、或执行typeof或事件delete);

在您的情况下,变量的 LexicalBinding 被异常中断。

let a = werwr // Uncaught ReferenceError: werwr is not defined

请点击链接并阅读更多相关信息。如果您找到恢复 varaible 的方法a,请告诉我。谢谢,今天我发现了一些关于 Javascript 的新东西。

感谢您的研究。如果您有兴趣,Bergi 的回答非常彻底
2021-05-02 07:06:28
你是对的,我现在的最终解释是 Chrome 开发者工具通过 eval() 运行你的脚本。我试图以此为例子。但是,let在 eval() 中......好吧。祝你好运。
2021-05-12 07:06:28
如果是这种情况,我应该能够执行“a =”合法字符串”,但它抛出了未定义 a 的错误。此外,如果我像您一样单独编写语句,我可以重新分配 a。但是在我的情况下,这没有发生
2021-05-14 07:06:28