Sed Seyedi

↳ On programming, startups and more.
blog projects about contact

JavaScript Hoisting Explained

Hoisting in JavaScript means that variables in the current scope are defined ahead of time by compiler.

Lets work with this following code:

console.log(x); // x is undefined  (VALUE)
console.log(y); // y is undeclared (ERROR! Your program halts!)
var x = 3;      // x is now 3!

Programmers don't have any problem with the concept of having to define a variable before trying to use it. But, why does JS Engine treat x and y differntly?

Undefined vs Undeclared

In most programming languages we would expect both console.log(x) and console.log(y) return an error saying these two variables are not defined. But, that's not the case for the JavaScript.

To the JS Engine, undeclared is what it sounds like: something (in this case a variable) that is not declared in your code -- something that does not exist in the world -- and of course, it has no value. We don't disagree with JS on why y should be undeclared. You would probably look at line #1 and think, good JS Engine, x is also not declared, YET. And the key of understanding hoisting is in that bold "yet".

JS is Compiled

OK, before you bite my words, I agree, we don't compile JS to machine code like we do with something like Go, Chicken, Rust, C++ or our good old C. But, trust me, it really helps to know that JS Engine does not go over your code one line at a time! It goes through your entire source code several times (mostly to optimize your code) before it starts executing it line by line.

Now, I'm sure you can link the bold YET to this. On line #1 JS already has x declared. But wait a minute, if it exists, what is its value? undefined. undefined is not an error! It is a real value, like the number 1 or the string hello world. You can even set a variable to the value of undefined yourself, just like we are used to set variables to null in other programming languages.

Null vs Undefined

Null and Undefined are different values in JS but used for the same purpose -- to declare a variable has no useful value! My guess is that the designer of JS first made Undefined and then decided to also add Null to make JS look more like Java.

Who Set our x to undefined

The JS engine, at the compile time! Why? It just want to allocate memory for variables. At one of the compile phases JS Engine goes through our entire source code and allocate memory for all of our declared variables and set their value to undefined. Remember, these values will be set in their scope. For our little example, everything is in the global scope.

Why not set it to 3?

I hear you, if it's trying to be smart why not go the extra mile and set the variable to its real value?

Because although it might be able to figure out primitive values for some variables it cannot figure out all of them without executing your code. In the following example x value should be the return value from the function foo() and who knows what needs to execute inside that function in order to get its value!

var y = 3;
var x = foo();

What about function statements?

Well, we know that in JS functions are first class (just like variables you can pass them around) that's why the idea of hoisting applies to them as well. Here is a simple example of how hoisting of functions work in JS, where we use a function before we define it.

foo(); // returns 3
function foo(){
    return 3;
}

Unlike variables, the compiler does not set the value of foo to undefined instead it points to the location where the function was defined. And a reference to the location of the function definition is all it takes for the runtime (when we execute the code line by line) to find and execute the function!

What about function expressions?

Well, unlike function statements, function expressions return some value and it is next to impossible for the compiler to cover all the edge cases of guessing if an expression is going to return a function or a primitive value. So, it just sets it to undefined like any other variable.

foo(); // Error: undefined is not a function!
var foo = function(){
    return 3;
};