【问题标题】:JavaScript catch parameter already definedJavaScript catch 参数已定义
【发布时间】:2011-08-31 07:26:21
【问题描述】:

我试图了解为什么我收到以下错误,而不是如何解决它。

将以下代码传递给JSLintJSHint 会产生错误'err' is already defined。

/*jslint white: true, devel: true, onevar: true, browser: true, undef: true, nomen: true, regexp: true, plusplus: true, windows: true, bitwise: true, newcap: true, strict: true, maxerr: 50, indent: 4 */
function xyzzy() {

    "use strict";

    try { /*Step 1*/ } catch (err) { }
    try { /*Step 2*/ } catch (err) { }

}

这里的明显假设是catch 的行为或应该像函数一样行为。因此,err 既不是全局变量,也不是xyzzy 的局部变量,而是catch 块的参数

在浏览ECMA-262 Standard 时,第 12.14 节描述 try 语句 表明 catch 子句采用 标识符,该标识符是 绑定的 em> 例外。此外,catch 的语义生成规则引用了一个 parameter,该参数通过调用 Identifier 作为 argument

这似乎向普通读者表明上述代码是有效的,并且可能 lint 工具存在错误。

即使IntelliJ 最严格的 JavaScript 代码检查分析也没有报告 err 被重新定义存在问题。

更多的问题是,如果它是一个变量范围问题,那么人们可能会推测err 正在流入全局空间,这会带来许多其他问题,而应该提前声明它,像这样:

/*jslint white: true, devel: true, onevar: true, browser: true, undef: true, nomen: true, regexp: true, plusplus: true, windows: true, bitwise: true, newcap: true, strict: true, maxerr: 50, indent: 4 */
function xyzzy() {

    "use strict";
    var err;  // DECLARE err SO IT IS CERTAINLY LOCAL

    try { /*Step 1*/ } catch (err) { }
    try { /*Step 2*/ } catch (err) { }

}

但这只会在每个 catch 语句中导致两个关于 err 的错误,使问题变得更糟,并可能引入 variable shadowing

lint 工具建议每个catch 块不仅引入它自己的词法范围,还引入一个新变量。这不可能。

简单地制作err1err2,...来安抚静态分析工具只会隐藏症状,不会有助于更简洁的代码。

JavaScript 专家:这是 lint 工具中的错误,JavaScript 规范的一个黑暗角落,还是对这里发生的事情的根本误解?

更新:写信给 JSLint 的作者 Douglas Crockford,事实证明这个警告是有充分理由的。请参阅下面的答案。

【问题讨论】:

  • 限制对来自 JSLint 的投诉的信任程度非常重要。
  • @Pointy - 理解,但迄今为止,JSLint 在涉及微妙问题时已经提出了一些非常有效的观点,这就是我使用它的原因。如果它实际上是一个错误,我会像过去一样联系 Douglas,但我想在这样做之前我会进一步探索这个问题。它总是有助于从内到外理解一种语言,以及 linter 产生的警告。只有当我知道 lint 工具出错时,我才会忽略这些警告,虽然不太可能,但这是可能的。
  • 提醒所有人:目标不是关于范围、catch 块中发生的事情或其他变量的阴影。这就是为什么 lint 将要捕获的参数视为唯一的变量声明。是否有一个微妙的用例为什么 lint 会这样做?如果它是合法的,仅仅忽略它是不够的,但如果这样做的做法可能会引入一个微妙的错误。这就是探索,这反过来又决定了适当的修复。
  • 好吧,规范很清楚标识符绑定在“新的声明性环境”中,因此必须将其视为样式注释,而不是对实际危险代码的警告。 (当然,有人可能会认为有问题的编码风格是“危险的”,但这是在意见范围内。)(在我看来 :-)
  • @Point - 我肯定会接受“风格”作为可接受的答案;我只需要弄清楚这种风格会出现问题的“原因”。 (见过 JSLint 如何处理 switch/case 语句缩进吗?感觉很陌生,过了一会儿才有意义。)我认为检查是在某人被某些非常邪恶和微妙的东西咬伤之后添加的。我很想知道那是什么。

标签: javascript scope try-catch jslint


【解决方案1】:

就这个问题写信给 JSLint 的作者 Douglas Crockford

毕竟有一个非常正当的理由......

道格拉斯写道:

Catch 变量的范围不正确,因此我建议您在每个变量中使用不同的名称。

如果您查看this similar StackOverflow question,您会注意到PleaseStand 开始接触它。 并非所有浏览器,尤其是历史悠久的浏览器,都能正确或一致地处理范围。

JSLint 识别出您的代码可能在一个浏览器中工作,但在另一个浏览器中却不行,从而留下一个非常讨厌和微妙的错误来追踪。 警告是真实的。

通过使用不同的名称,是的,它根本感觉不干净或简洁,因为它不是,恰好是不遇到问题的通用方法。

感谢 Douglas 的澄清!谜团解开了。

【讨论】:

    【解决方案2】:

    规范非常清楚,定义为 catch 语句的任何名称都只会遮蔽周围的名称。除此之外,我不会将这些错误视为警告。仅凭直觉,我相信这只是对那些 Lint 工具的设计者的过度热心分析。

    由于 catch 块引入了一个新的作用域,使用相同的名称只会掩盖封闭作用域中的任何相似名称。如果您了解语义,这不一定是坏事。如果您在假设封闭的err 可以访问的情况下进行编码,那么您需要更改您的假设。

    规格

    产生式 Catch : catch ( Identifier ) 块的评估如下:

    1. 令 C 为已传递给此产生式的参数。
    2. 设 oldEnv 为正在运行的执行上下文的 LexicalEnvironment。
    3. 让 catchEnv 成为调用 NewDeclarativeEnvironment 并传递 oldEnv 作为参数的结果。
    4. 调用 catchEnv 的 CreateMutableBinding 具体方法,传递 Identifier String 值作为参数。
    5. 调用 catchEnv 的 SetMutableBinding 具体方法,传递 Identifier、C 和 false 作为参数。请注意,最后一个参数在这种情况下无关紧要。
    6. 将正在运行的执行上下文的 LexicalEnvironment 设置为 catchEnv。
    7. 设 B 为 Block 求值的结果。
    8. 将正在运行的执行上下文的 LexicalEnvironment 设置为 oldEnv。
    9. 返回 B.

    注意无论控制如何离开 Block,LexicalEnvironment 总是会恢复到之前的状态。

    【讨论】:

    • 第二个代码块只是一个测试,看看这是否有助于或损害问题。 (很痛。) JSLint 试图告诉我一些事情或避开我可能会伤害自己的地方,我想知道那是什么——不要忽视警告。实际代码不会有局部变量。
    • 您是否有参考说明 catch 块明确引入了新范围?不是怀疑你,只是想了解一下。
    • @JaredPar:Catch 块不会引入新的变量环境,在 ES3 中它们只是在作用域链的顶部引入一个对象,该对象具有名为 catch 标识符的属性及其值是已经引发的异常,在 ES5 中它们引入了 LexicalEnvironment,但又不是“新范围”(变量和函数声明总是被提升)。
    • Catch 没有引入新的作用域。 catch (e) { ... this is not a new scope ... }jsfiddle.net/robert/sZrGb
    • 重读他的答案,我认为他的意思是 (err) 将被限定为 catch 块,这是真的,但 catch 块本身不是一个作用域。也许在你的回答中澄清这一点。
    【解决方案3】:

    看看这个答案: JSLint complaining about my try/catch

    如前所述,try 打开一个新的块作用域。见https://developer.mozilla.org/en/JavaScript/Reference/Scope_Cheatsheet

    确实,文档的顶部解释说它并不都是标准的,但在ES5, section 12.14 中执行catch 块的部分清楚地将MDC 的描述定义为标准:

    无论控制如何离开 Block,LexicalEnvironment 总是会恢复到之前的状态。

    【讨论】:

    • 我希望我事先遇到过这个问题,因为我肯定会提到它。我能够推断出 JSLint 在做什么,但我没有从那篇文章的答案中得到原因。我想也许是时候打破猜测,问问克罗克福德了。我会让每个人都知道我发现了什么。
    • 您的 Scope_Cheatsheet 链接进一步支持err 是一个参数而不是变量的想法。我开始怀疑这只是一个 lint bug。我已经问过道格拉斯,他是否可以解释其背后是否有我没有看到的原因。他通常非常擅长周转时间。
    【解决方案4】:

    如果您以后需要参考错误,您可能在连续的 try-catch 中重复使用相同的参数名称时会遇到问题。

    如果调用了多个 catch,则只有最后一个处于 finally 或函数范围表达式的范围内。

    jsLint 是保守的 - 如果你可以防止 可能 搞砸一个独特的变量,为什么不使用它?

    【讨论】:

    • 理想情况下,不应尝试根据传入的参数访问另一个 catch 块中的错误;如果需要,将其松鼠。根据戴文回答中的 Scope_Cheatsheet ,这甚至是不可能的。正如 Crocker 指出的那样,并非所有浏览器都以相同的方式处理范围(阅读了一些 IE8 和更旧的材料)。我们刚刚了解到 JSLint 并不保守,它实际上是正确的——特别是如果您想处理历史悠久的、不兼容的浏览器。自然的直觉反应不是引入不必要的符号,但这个“技巧”解决了一个更大的问题。
    猜你喜欢
    • 2019-01-05
    • 1970-01-01
    • 2012-05-12
    • 1970-01-01
    • 2017-06-29
    • 1970-01-01
    • 2014-03-04
    • 2011-09-25
    • 1970-01-01
    相关资源
    最近更新 更多