最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

scope - JavaScript catch parameter already defined - Stack Overflow

programmeradmin7浏览0评论

I'm trying to understand why I'm getting the following error, not how to work around it.

Passing the following code to JSLint or JSHint yields the error '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) { }

}

The obvious assumption here is that catch behaves, or should behave, like a function. Thus, err is neither a global variable, nor a local variable to xyzzy, but a parameter for the catch block.

In browsing the ECMA-262 Standard, section 12.14 describing The try Statement indicates that the catch clause takes an Identifier that is bound to an exception. Additionally the semantic production rule for catch refers to a parameter that's passed calling out Identifier as an argument.

This seems to suggest to the casual reader that the above code is valid and that perhaps the lint tools have a bug.

Even IntelliJ's strictest JavaScript code inspection analysis doesn't report there being a problem with err being redefined.

More of a concern, if it is a variable scoping concern, then one might surmise that the err is bleeding into the global space, which poses a whole host of other problems, and that instead one should declare it up front, like this:

/*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) { }

}

But this only results now in two errors about err at each of the catch statements, making the problem worse and potentially introducing variable shadowing.

The lint tools are suggesting that each catch block introduces not just it's own lexical scope, but a new variable as well. This can't be right.

Simply making err1, err2, ... to placate the static analysis tools merely hides the symptom and doesn't contribute to cleaner code.

JavaScript Gurus: Is this a bug in the lint tool, a dark corner with the JavaScript spec, or a fundamental misunderstanding of what's happening here?

UPDATE: Wrote to Douglas Crockford, author of JSLint, and it turns out there's a very valid reason for this warning. See answer below.

I'm trying to understand why I'm getting the following error, not how to work around it.

Passing the following code to JSLint or JSHint yields the error '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) { }

}

The obvious assumption here is that catch behaves, or should behave, like a function. Thus, err is neither a global variable, nor a local variable to xyzzy, but a parameter for the catch block.

In browsing the ECMA-262 Standard, section 12.14 describing The try Statement indicates that the catch clause takes an Identifier that is bound to an exception. Additionally the semantic production rule for catch refers to a parameter that's passed calling out Identifier as an argument.

This seems to suggest to the casual reader that the above code is valid and that perhaps the lint tools have a bug.

Even IntelliJ's strictest JavaScript code inspection analysis doesn't report there being a problem with err being redefined.

More of a concern, if it is a variable scoping concern, then one might surmise that the err is bleeding into the global space, which poses a whole host of other problems, and that instead one should declare it up front, like this:

/*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) { }

}

But this only results now in two errors about err at each of the catch statements, making the problem worse and potentially introducing variable shadowing.

The lint tools are suggesting that each catch block introduces not just it's own lexical scope, but a new variable as well. This can't be right.

Simply making err1, err2, ... to placate the static analysis tools merely hides the symptom and doesn't contribute to cleaner code.

JavaScript Gurus: Is this a bug in the lint tool, a dark corner with the JavaScript spec, or a fundamental misunderstanding of what's happening here?

UPDATE: Wrote to Douglas Crockford, author of JSLint, and it turns out there's a very valid reason for this warning. See answer below.

Share Improve this question edited May 25, 2011 at 13:13 Walt Stoneburner asked May 23, 2011 at 16:37 Walt StoneburnerWalt Stoneburner 2,6124 gold badges26 silver badges40 bronze badges 6
  • 4 It's important to limit the amount of credence given to plaints from JSLint. – Pointy Commented May 23, 2011 at 16:40
  • 1 @Pointy - understood, but to date JSLint has raised some very valid points when it's e to subtle issues, which is why I use it. If it's actually a bug, I'll contact Douglas as I have in the past, but I figured I'd explore the issue further before doing so. It always helps to understand a language inside and out, as well as the warnings a linter produces. I'll ignore those warnings only when I know the lint tool is wrong, which is possible, though unlikely. – Walt Stoneburner Commented May 23, 2011 at 16:53
  • REMINDER ALL: The goal is is not about scope, what happens in the catch block, or shadowing of other variables. It's about why is lint treating a parameter to catch as if it were a unique variable declaration. Is there a subtle use case why lint would do this? It's not just enough to ignore it if it's legal, but if the practice of doing the above could introduce a subtle bug. That's the exploration, which in turn dictates an appropriate fix. – Walt Stoneburner Commented May 23, 2011 at 17:03
  • Well the spec is pretty clear that the identifier is bound in a "new declarative environment", so this must be considered a style ment rather than a warning about actually dangerous code. (Of course, one might consider questionable coding style to be "dangerous", but it's in the realm of opinion.) (in my opinion :-) – Pointy Commented May 23, 2011 at 17:03
  • @Point - I'll certainly take "style" as an acceptable answer; I just need to figure out the 'why' that style would be problematic. (Ever see how JSLint treats switch/case statement indentations? Feels foreign, makes sense after a while.) I figure the check was added after someone, somewhere got bitten by something really evil and subtle. I'd love to know what that was. – Walt Stoneburner Commented May 23, 2011 at 17:15
 |  Show 1 more ment

4 Answers 4

Reset to default 11

Wrote to Douglas Crockford, author of JSLint, about this issue.

There turns out to be a very valid reason after all...

Douglas writes:

Catch variables are not scoped correctly, so I remend that you use a different name in each one.

If you look at this similar StackOverflow question you'll note that PleaseStand started to touch on it. Not all browsers, especially historic ones, handle scoping correctly or consistently.

JSLint recognizes that your code may work in one browser, but not in another, leaving a very nasty and subtle bug to track down. The warning is real.

By using a different name, which, yes, doesn't feel clean or concise at all, because it isn't, happens to be the universal way of not running into the problem.

Thank you Douglas for clarifying! Mystery solved.

The specification is quite clear that any name defined a catch statement will do nothing more than shadow a surrounding name. Beyond that I would not consider these errors as nothing more than a warning. Just using pure intuition I believe that this is simply overzealous analysis on the part of the designer of those Lint tools.

Since a catch block introduces a new scope, using the same name will simply shadow any similar names in the enclosing scope. This isn't necessarily a bad thing if you are aware of the semantics. If you are coding under the assumption that the enclosing err will be accessible than you'll need to change your assumptions.

Specification

The production Catch : catch ( Identifier ) Block is evaluated as follows:

  1. Let C be the parameter that has been passed to this production.
  2. Let oldEnv be the running execution context’s LexicalEnvironment.
  3. Let catchEnv be the result of calling NewDeclarativeEnvironment passing oldEnv as the argument.
  4. Call the CreateMutableBinding concrete method of catchEnv passing the Identifier String value as the argument.
  5. Call the SetMutableBinding concrete method of catchEnv passing the Identifier, C, and false as arguments. Note that the last argument is immaterial in this situation.
  6. Set the running execution context’s LexicalEnvironment to catchEnv.
  7. Let B be the result of evaluating Block.
  8. Set the running execution context’s LexicalEnvironment to oldEnv.
  9. Return B.

NOTE No matter how control leaves the Block the LexicalEnvironment is always restored to its former state.

Check this answer out: JSLint plaining about my try/catch

As has mentioned, try opens a new block-scope. See https://developer.mozilla/en/JavaScript/Reference/Scope_Cheatsheet

Indeed the top of the document explains that it's not all standard, but in ES5, section 12.14 the section on executing a catch block clearly defines MDC's description as standard:

No matter how control leaves the Block the LexicalEnvironment is always restored to its former state.

You can have a problem with reusing the same parameter name in sequential try-catches if you later need to refer to the error.

If more than one catch was called, only the last one will be in scope for a finally or function scope expression.

jsLint is conservative- if you can prevent a possible screw up with a unique variable, why not use it?

发布评论

评论列表(0)

  1. 暂无评论