I am developing with JavaScript for a while and I am used to a lot of it quirks. But even after years of using this language you still find new curiosities. Today a struggled a while with this one.
Considering this piece of JavaScript code:
-
var i = 10;
-
function foo() {
-
i++; // i is now 11
-
console.log("foo1: "+i);
-
i = 0; // i is now 0
-
console.log("foo2: "+i);
-
};
-
console.log("global1: "+i); // should be 10
-
foo();
-
console.log("global2: "+i); // and now 0
Ok. That’s fine. Now we change the code a little bit to this one:
-
var i = 10;
-
function foo() {
-
i++; // is now 11
-
console.log("foo1: "+i);
-
var i = 0; //create a new variable with value 0
-
console.log("foo2: "+i);
-
};
-
console.log("global1: "+i); // expect: 10
-
foo();
-
console.log("global2: "+i); // expect: 11
What do you expect? I was quite surprised:
global1: 10
foo1: NaN
foo2: 0
global2: 10
Why do we get a Not a Number? Why is JavaScript not using the global var as in the first example? Why is the global variable i not increased, the operation took place before the declaration of the local variable i? JavaScript has some “strange” rules about variable declaration. Every variable declared within a function is initialised at the beginning of the function, so our function actually would look like this:
-
var i = 10;
-
function foo() {
-
var i; //undefined
-
i++; // undefined + 1 = NaN
-
console.log("foo1: "+i);
-
i = 0; //and now 0
-
console.log("foo2: "+i);
-
};
-
console.log("global1: "+i); //10
-
foo();
-
console.log("global2: "+i);//still 10, because global i has not changed
Looking into the rules of JSLint which helps to verify JavaScript code to improve the quality of your JavaScript you get a few rules how to safely declare variables:
- JavaScript allows var definitions to occur anywhere within a function. JSLint is more strict.
- JSLint expects that a var will be declared only once, and that it will be declared before it is used.
- JSLint expects that a function will be declared before it is used.
- JSLint expects that parameters will not also be declared as vars.
- JSLint does not expect the arguments array to be declared as a var.
- JSLint does not expect that a var will be defined in a block. This is because JavaScript blocks do not have block scope. This can have unexpected consequences. Define all variables at the top of the function.
So Remember: Variables are not initialised when they are declared!
Links:
- all JSLint rules for quality JavaScript code.
- WTFJS.com, a page collection all kinds of JavaScript curiosities.
Comments