Behind the scenes of Hoisting in JavaScript
Understanding the depths of execution context and its phases
The Misunderstanding - Mugging up
Hoisting in JavaScript is considered to be one of the most misunderstood topic. I've personally seen many people mugging up the functionality of hoisting - "Function definitions in JavaScript are sent to the top of the file" or "A function can be called before its function definition" without understanding the meaning behind the way it works like that.
The Background - Execution context in JS
Before we dive deep into hoisting, let us try understanding how exactly a JavaScript program runs. A JavaScript program which runs on any runtime, an execution context is created, to be more precise, it is called the Global Execution Context (G.E.C).
Execution context basically has two components:
The memory component (a.k.a. variable environment): It stores all the variables and and the functions of program in key-value pairs.
The code component (a.k.a. thread of execution): This place is know to execute the code line by line (explains the synchronous and single-threaded nature of JavaScript)
The Working - Talk is cheap, let's see the code
Well, enough of theory, let's consider a code snippet as an example and try to understand whatever we learnt so far.
var num = 10
function sum(num1, num2) {
var sum = num1 + num2;
return sum;
}
var sum1 = sum(n, n);
var sum2 = sum(2, 4);
console.log(sum1, sum2);
In the above code snippet, these are the steps that take place when the code is executed:
A Global Execution Context when a JavaScript code runs for the first time.
Now the program goes through two phases:
Memory creation (takes place in "memory" component)
Code execution (takes place in "code" component)
Memory creation phase:
Memory is allocated to each variable and function in JavaScript program.
Variables and functions are stored in memory as key-value pairs.
For variables, they are stored as, <variable_name>: undefined
For functions, they are stored as, <function_name>: <function body>
- Code execution phase:
Each line of the program is executed line by line in the code component.
Whenever there are function calls, a new local function execution context is created, which has its dedicated "memory" and "code" component.
Here is the step by step process of what exactly happens in the code execution phase:
STEP-1 : Code execution starts line by line
STEP-2: Function execution context is created
STEP-3.1 : Code execution in function execution context starts
STEP-3.2: Once function returns, local function execution context is destroyed
STEP-4 : Similarly sum2 is evaluated by creating local function execution context and it is destroyed
STEP-5: Last line of code is executed and sum1, sum2 are logged in console
Finally the Global execution context is destroyed on execution of the last line of code.
This is how a program in JavaScript is executed, as we discussed earlier, everything in JavaScript happens inside an execution context! All these execution contexts (global as well as function) are managed by a call stack. The G.E.C always lies on the bottom of the call stack, since it is the first execution context being created, it is the first to be placed in the stack.
The Hoisting - The What and How of "Hoisting"
The whole idea of hoisting lies in the concept of execution context in JavaScript. In the memory creation phase of the program, all the variables and functions are allocated memory before code execution. So this is considered as hoisting!
We can call the functions before the function definition (since function is pointing to the function body and has memory allocated)
sum(1, 2) function sum (num1, num2) { return num1 + num2; }
We can use variables before their declaration, it doesn't give us reference error, instead undefined is given when we try to access a variable before it's declaration in code
console.log(num1); var num1 = 10;
The default behaviour of moving all the declarations at the top of the scope before code execution is officially known as hoisting, this is not actually done, but we can imagine that's how it is done (deep down we know what exactly is happening, the concept of execution context and memory creation phase in it!)
It's still surprising to know how people believe the definition to be true and think that is what actually happens. Well, you know the breadths and depths of hoisting now.
Homework: Now you are going to figure out why hoisting doesn't work for arrow functions in JavaScript. (It's actually very simple, just imagine the memory creation phase and you'll know arrow function is just a function stored in a variable, so JS treats it as a variable, well I almost gave the answer ๐)
Happy coding ๐