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.