Talking about Javascript.
Have you ever wondered about the origin of the bizarre magic that allows JavaScript programmers to use variables and functions before they are declared?
It’s called “hoisting” and it’s the process of lifting all variable declarations using var
– remember, only using var
– to the top of their scope when JavaScript is compiled.
And it’s not over: once all var
variabile declarations have been hoisted, it’s time to hoist functions declarations, which move to the very top of their scope, even above variables.
Ok, stop the train, I want to get off, you are not saying JavaScript is compiled, are you? Everybody knows JavaScript is an interpreted language, not a compiled language.
Well, sort of. When JavaScript code is executed, the interpreter is capable of looking for all var
variable declarations and all functions declarations and bring them somehow at the top of their scope.
Basically we can say the interpreter runs twice: the first time adjusting a bit your script, if needed, and the second time actually executing it line by line as we all expect from an interpreted language.
Your script, I mean your actual script in your text editor, does not change, it’s just that JavaScript interpreter performs hoisting somewhere under the hood, so in the end we can say the interpreter is working on some kind of rearranged – or compiled – script.
Let’s have a look at a couple of examples.
If you execute this simple script:
console.log(a);
The result will be:
Uncaught ReferenceError: a is not defined
This happens because a
variable has never been defined.
Now let’s try with:
console.log(a);
var a = 10;
As you can see, we try to log a variable before it’s declared, so we should get the same error, but actually we get:
undefined
Why? Because of hoisting. This is the compiled script ran by the interpreter:
var a;
console.log(a);
a = 10;
Now, that undefined
output makes much more sense.
Now let’s make things more difficult:
b();
We are trying to execute a function which is not declared, so we get
Uncaught ReferenceError: b is not defined
Thanks to hoisting, we can fix the script this way:
b();
function b() {}
And this will work, although the function has been declared after we call it. And this is where troubles start:
var c = b();
function b() {
a = 100;
return (a / 2);
}
console.log(c);
console.log(a);
We are expecting c
to be 50, and a
to fire a reference error because we never declared it outside the scope of b
. But…
50
100
That’s because if you assign an undeclared variable, that is if you omit the var
keyword, JavaScript automatically assigns it to the global scope.
There is another interesting case to cover:
var a = 100;
b();
function b() {
console.log(a)
var a = 200;
console.log(a);
}
Here you might expect the output at line 4 to be 100, just like you declared it at line 1, but we get
undefined
200
Because the declaration of local a
variable at line 5 hoists in its inner scope inside the function.
Also keep in mind hoisting occurs only to function declarations, and not to function expressions:
b();
c();
function b() {};
var a = function c() {};
Throws
Uncaught ReferenceError: c is not defined
As you can see, hoisting can cause some headache, so here are my two advices not to run in trouble with it:
1 – Remember to declare your variables at the very top of your script. This will make it look like the script the machine is going to interpret.
2 – Always declare variables, not just using var
, but above all using const
and let
, which do not allow hoisting.
Happy coding.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.