【问题标题】:Chrome console already declared variables throw undefined reference errors for letChrome 控制台已经声明的变量会为 let 抛出未定义的引用错误
【发布时间】:2016-12-21 04:26:09
【问题描述】:

最近我在 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 语句与隐式 case 之间似乎存在一些差异。

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

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

let example
throw new Error
example = 5

【问题讨论】:

  • @JaromandaX 修正错别字抱歉
  • 看起来 chrome 中的控制台一开始就处于严格模式 - Firefox 中的类似问题,除了 a = "legit string" 每次都有效
  • 这似乎是 chrome 控制台中的一个烦恼,当然 - 但是,这不会对网页产生任何影响
  • @JaromandaX 谢谢好奇。这有点烦人,因为如果发生这种情况,我每次都必须刷新。由于这些错误,我无法重新定义 a

标签: javascript scope ecmascript-6 google-chrome-devtools let


【解决方案1】:

当您将临时死区引入全局范围时,就会发生这种情况。您可能知道,let 声明 are hoisted but left uninitialised。由于控制流,变量可能从未初始化:

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;

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

这个was discussed on es-discuss,但被视为无关紧要。如果顶级<script> 执行引发错误,则问题比未初始化的变量更大。没有恢复的路。如果你需要一个(例如,通过尝试在连续的脚本中重新声明它),你必须使用var

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

【讨论】:

  • 考虑到 let 被提升了,我写了这样的代码:" let x; throw new Error; x = 6" 在这种情况下,x 实际上可以稍后重新分配。 “手动”提升声明语句与隐式实现之间是否有任何细微差别?我以更好的格式添加了对该问题的后续处理
  • 是的,let x;let x = undefined; 的快捷方式,如果您手动将其放在顶部,您将在抛出异常之前初始化变量。
  • 回复“没有恢复的路”,以后可能可以<script onerror
  • @Pacerier 您可以处理错误(今天已经使用window.onerror)并记录它,但您无法恢复它。在抛出异常之前无法撤消脚本所做的事情,也无法取消声明变量。
【解决方案2】:

你应该知道 JS 中的提升。基本上,像这样的声明 let a = werwr;

被解释为 let a; a = werwr;

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

更新

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

let 和 const 声明定义范围为 运行执行上下文的 LexicalEnvironment。变量是 当它们包含的词法环境被实例化时创建但是 在变量的 LexicalBinding 为 评估。由带有初始化程序的 LexicalBinding 定义的变量 被赋予其初始化器的AssignmentExpression的值时 LexicalBinding 被评估,而不是在创建变量时。如果 let 声明中的 LexicalBinding 没有 Initializer 当 LexicalBinding 时,变量被赋值为 undefined 被评估。

...

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

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

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

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

【讨论】:

  • 如果是这样的话,我应该可以做"a = "合法字符串",但是它抛出了a未定义的错误。如果我像你一样单独编写语句,我可以重新分配 a。但是在我的情况下,它没有发生
  • 你说得对,我现在的最终解释是 Chrome 开发者工具通过 eval() 运行你的脚本。我试图以此为例。但是,let 在 eval().... 内。祝你好运。
  • 感谢您进行研究。如果您有兴趣,Bergi 的回答非常详尽
猜你喜欢
  • 2017-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-04
  • 1970-01-01
  • 2020-11-06
  • 2016-03-17
相关资源
最近更新 更多