JavaScript: Variable declarations and scope 5


In a lot of programming languages, I would even say in most programming languages, the following looks awkward and does surely not work. One of those languages is C. But JavaScript is sometimes a miracle and like a box of jewelery, ready to be discovered. And if you are new to it, it is loaded with surprises. Read on to get a tiny glimpse into some of them. JavaScript has some really interesting things waiting.

Get started

The first one serves as an eye opener. If you are a long-time and experienced JavaScript hacker you will know how this works.

for (var i=0; i<Infinity; i++){
  if (i==47) break;
}
console.log(i); // 47

In line 4 the number 47 is printed into the console. Even though the variable “i” has been declared inside the for-loop. Yes, it does, believe me. There is only one thing in JavaScript that creates a scope, it’s the function (in strict mode in ECMAScript5 there are more ways though). The code you put inside function(){…} does get it’s own scope, though with the access to it’s surrounding scope. Btw, this is also why you very often see the “self = this” quirk.

The actual “problem”

But let’s get to the actual code, that lead me to write this blog article. I love those tricky things, that give you insight into the language and sometimes make you read the specification. I guess JavaScript is the language where the ratio of “just-users” to “people who really understand the entire language”, is very high. I would not consider myself to understand the language entirely.

Consider the following code.

var i = 47;
console.log(i); // 47

function test(){
  console.log(i); // undefined
  i = 1
  console.log(i); // 1
  console.log(window.i); // 47
  if (false){
    var i = 0;
  }
}
test();

console.log(i); // 47

Note: Testing this will only work in the browser, not in the console, since “window” is only declared in the browser context!

The most interesting part is definitely line 5. I will dive into it deeper later. Line 7 and 8 show that we are operating on two different variables, both with the name “i”. The one printed out in line 7 and the one printed in line 8. The variable referred to by “window.i” is the one we declared in line 1, this is no suprise. But that the assignment in line 6 didn’t override the value of it with 1 is interesting though, right?
Let’s break it down a bit further.

var i = 1;
function test(){
  console.log(i); // undefined
  var i;
}
test();

This is the essence of the code above. Even though the variable declaration takes place in line 4, after the console.log() call, the variable is already declared when console.log() is called. It is not referencing the global variable with the value 1 though!
After reading through various parts of the ECMAScript specification, this might be the one that indirectly says that this is specified as stated above:

For each VariableDeclaration and VariableDeclarationNoIn d in code, in source text order do …

ECMAScript Spec (version 5), section 10.5, page 59.
This is from the section that explains how to setup bindings and knowing that a VariableDeclaration is “var i” this can be understood as: upon creating a function do just the variable declarations found in code.

What the heck? Ok, let’s approach from another angle, which I find even more interesting. It is the fact, that the statement “var i = 0″ is effectively broken apart in the following code:

var i = 1;
function test(){
  console.log(i); // undefined
  var i = 0;
}
test();

On line 3 it will still print out “undefined”, because the interpreter did find the variable declaration in line 4. We know that because it prints “undefined” and not 1. But it did not do the variable assignment, which is also contained in line 4. So it actually broke apart the code on line 4 “var i = 0″ into 1) “var i” which it executes when creating the function and 2) the assignment “i = 0″ when the execution comes to line 4.
The ECMAScript Spec (version 5) states the separation of those two cycles explicitly too, page 87:

A variable with an Initialiser is assigned the value of its AssignmentExpression when the VariableStatement is executed, not when the variable is created.

Get trapped

In our actual example above we had another nicety, which I will try to dissect in the following examples:

console.log(i); // ReferenceError: Can't find variable: i

This will throw an error “ReferenceError: Can’t find variable: i”. Though

console.log(i); // undefined
var i;

will not throw an error, it will print “undefined” peacefully, we know that and have learned that before and read that in the spec too. (Just wanted to make it clear again :-))

But if we do the following

console.log(i); // undefined
i = 1;
if (false){
  var i;
}

we still get “undefined” in line 1. Ooops. Did you expect that? Does the “if” in line 3 not nest the variable declaration and create it only inside the scope of the “if”? No it doesn’t. As mentioned before, there is only one thing that creates scope, that is a function. So even, this meaningless condition is relevant here.
It would be interesting to test what JavaScript compilers, like shrinksafe and Google’s closure make out of it. If the removed the code from line 3 through line 5 than this would change the semantics of this code dramatically.

Have fun playing around with this code and twisting your brain.

Testing on the console

Please note! Watch when just copy+pasting the code above into the console of your browser, they have each different behavior depending on which browser you are using. If you always wrap the code in an extra (function(){…})() you will be save, but otherwise the console will give you differing results. Or, the best way, is to put the examples in a website to test it.


About Wolfram Kriesing

Wolfram Kriesing has more than fourteen years professional experience in IT. The early involvement in web technologies provides him with deep knowledge and experience for designing and implementing stable and scalable architectures.

  • Tjerk Wolterink

    Ah nice, i knew about function scope but i did not new about the javascript interpreter checking all declaration before execution. Again it’s all about semantics, that’s why a specification is so important. Too bad the ecmascript specifications are written in plain text. This increases the chances of different interpretations and ambiguity.

    For example what do you think the value of x is after running this code?

    var b=10;
    var f=function() {
    var b=5;
    function g() {
    var b=8;
    return this.b;
    }
    return g();
    };
    var x = f();

    Before running it try to run it in your head and write down the value of x. Then run it in a javscript engine ;-)

    Semantics specifications *should* be written in a formal language like Structural Operational Semantics… but then only a few people can actually read them ;-)

    Check SOS description of JS here! (the example was also from there)
    http://www.stanford.edu/class/cs242/slides/OperSemanticsJS

    But again thanks for this insight, i think it’s one of the bad things of javascript ;-)
    Greetings to Nikolai!

  • Jack Tanner

    This is called ‘hoisting’ the declaration.

    For example, see http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting

  • http://customer.immowelt.de/bildergalerie/heinzvonheiden/ Heinz von Heiden

    Conspicuous site, pls contiune your work and keep us update with more storys

  • http://www.ecmpartner.pl Biuro Rachunkowe wrocław

    I just found this blog a while back when a friend suggested it to me. I have been a regular reader ever since.

  • http://twitter.com/cintakvimi cin takvimi

    Keep up the fantastic work , I read few content on this internet site and I conceive that your web blog is very interesting and holds circles of fantastic information.