More weird JavaScript: variable scope

I know I'm going to get "only a n00b would not know that" comments, but I'm sure this will help some folks out there, so here it goes.

Eilon pointed out to me this morning this weird and very counter-intuitive behavior of JavaScript. Basically, there is no block scope for variables...

var x=1;
if (true) {
    var x=2;
    if (true) {
        var x=3;
        alert(x);
    }
    alert(x);
}
alert(x);

outputs 3 three times, whereas in all other curly brace languages that I know, you'd get 3, 2 and 1.

Except that there is variable scope... Only in functions and script blocks. That's what enables OO programming using closures.

To summarize: variables in JavaScript are only local to functions, not to any other kind of {} block.

If anybody has a good explanation for why JavaScript goes against all tradition from its ancestor language (C), I'd be genuinely interested in knowing it.

(thanks to David and Karsten for some insights on this issue)

10 Comments

  • I think this is especially error prone since it lets you put the var keyword in front of each "declaration". I don't know really know the full semantics of "var". I know that it declares a symbol to be a variable and that it is optional, but it seems like it might be useful for it to give some warning if you use it on a symbol more than once.



    Python uses the same scoping rules as JavaScript. It might have something to do with syntax associated with weak typing. Because you do not actually have to declare variables, there isn't a good way to tell when you want a new variable or reuse an old variable. So you have to attach the scoping rules exclusively to code blocks.

  • If you turn on strict mode in Firefox (which I recommend to all JS developers out there), you do get a warning for multiple declarations.

    I think you're right. I'm also suspecting that it has to do with the very nature of dynamic languages but the exact precise reason why they could do it for function blocks and not other kinds of blocks eludes me.

  • Ehm, where is the weirdness in this? C# works exactly the same, nested if blocks use the same scope.

  • Actually, C# won't let you compile that. BUT a variable is not accessible outside of its scope. This:

    {

    int x = 1;

    Response.Write(x);

    }

    Response.Write(x);

    will not compile, whereas JavaScript would be perfectly happy with it and write 1 twice.

  • I have been bitten a number of times using "i" in nested for loops without realising it. The inner loop overwrites the value of the outer loop.

  • Brian: I don''t think this one is especially weird. It *is* a bug factory, yes, but you need a way to reference global variables so this is more or less needed.

    If you run this in Firefox in script mode, it will issue a warning for the use of an undeclared variable, which really helps avoiding this kind of problem.

  • More good reason to have a client side version of C#.... :)

  • JB: I think we read the same blogs ;) . I've seen this one. The problem with it is that it uses nested closures. I'm fine with closures when used reasonably, but this one for me is too much. I think it's not very readable, it has all the dangers of closures and it's just not worth the trouble.

    I think it's best just to be conscious of this problem and deal with it without hacking around it.

  • Leave off the var operator and you will have your global scope. Don't var unless you want the variable to be local in scope.

  • Lonnie: if you do that, you'll get warnings from Firefox in strict mode. If you want global scope, you can use expandos off window. Unfortunately, EcmaScript doesn't standardize on a consistent global object, so this won't work in other hosts than browsers.

Comments have been disabled for this content.