Search code examples
javascriptv8spidermonkey

Confusing operation of JavaScript `var` keyword


I’ve run into a very strange (to me) problem with the var keyword. I’ve reduced it to a fairly minimal test case, and found it’s exhibited in Node.js (thus, V8 and Chrome), Safari 4’s inspector (thus, Nitro), and FireBug (obviously, SpiderMonkey). I was originally preparing a bug report, but since it’s so widely displayed, I’m going to assume that I completely misunderstand how JavaScript is supposed to scope and look up variables.

The test case is very small, and on GitHub here: http://gist.github.com/260067. The only difference between the first and second example is the inclusion of the var keyword.

Here, as well, is a similar test case that exhibits the same ‘problem’ in a different way: https://gist.github.com/698b977ee0de2f0ee54a

Edit: To preclude any more answers attempting to explain how cascading scope works, I’m intimately familiar with that. My problem, is that I don’t understand why the following code ‘works’ (in that it alert()s ‘outer,’ followed by ‘inner,’ and then again ‘outer’):

(function(){
  var foo = 'outer';
  alert("Outer `foo`: " + foo);

  (function(){
    foo = 'inner';
    alert("Inner `foo`: " + foo);

    var foo;
  })();

  alert("Outer `foo`: " + foo);
})();

The var foo; occurs in a completely irrelevant position to the re‐assignment of foo; so why does it affect that assignment in a very substantial way?


Solution

  • The thing is that unlike other languages, JavaScript creates all variables at the start of a function. This means that the code:

    (function(){
        if(myVar == undefined){
            alert(myVar);
        }
        if(myVar == undefined){
            var myVar = 5;
        }
    })();
    

    Is actually compiled and interpreted as

    (function(){
        var myVar;
        if(myVar == undefined){
            alert(myVar);
        }
        if(myVar == undefined){
            myVar = 5;
        }
    })();
    

    To create a variable and only have it available inside an if or loop block, you have to use let, which is a new JavaScript feature. I'm not sure how many browsers implement it yet (Firefox 3.5 does if you use <script type="text/javascript;version=1.7">).

    (function(){
        if(myVar == undefined){
            alert(myVar);
        }
        if(myVar == undefined){
            let myVar = 5;
        }
    })();