已经定义了JavaScript catch参数

时间:2011-05-23 16:37:43

标签: javascript scope try-catch jslint

我正在尝试理解为什么我收到以下错误,而不是 如何解决它。

将以下代码传递给JSLintJSHint会产生错误'错误'已定义。

/*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子句采用的是标识符 绑定到异常。此外,catch的语义生成规则是指参数,它将标识符作为参数传递出去。

这似乎向随意的读者建议上面的代码是有效的,也许lint工具有一个bug。

即使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,事实证明这是一个非常有效的理由。请参阅下面的答案。

4 个答案:

答案 0 :(得分:10)

写给JSLint的作者Douglas Crockford,关于这个问题。

毕竟这是一个非常正确的理由......

道格拉斯写道:

  

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

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

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

通过使用一个不同的名称,是的,根本不会感觉干净或简洁,因为它不是,恰好是没有遇到问题的普遍方式。

谢谢道格拉斯的澄清!神秘解决了。

答案 1 :(得分:3)

规范很清楚,任何定义了catch语句的名称都只会影响周围的名称。除此之外,我不认为这些错误仅仅是一个警告。只是使用纯粹的直觉我相信这只是那些Lint工具的设计者的过分热情分析。

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

<强>规范

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

  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。
  10. 注意无论控件如何离开Block,LexicalEnvironment总会恢复到以前的状态。

答案 2 :(得分:2)

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

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

事实上,文档的顶部解释说它不是全部标准,但在ES5, section 12.14中,执行catch块的部分明确将MDC的描述定义为标准:

  

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

答案 3 :(得分:0)

如果稍后需要引用错误,可以在顺序try-catches中重复使用相同的参数名称时出现问题。

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

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