Developers often have a love-hate relationship with JavaScript because sometimes it doesn't work as expected. Learning JavaScript fundamentals and it's quirks can assist developers in debugging and writing clean code. This blog post aims to explain one such peculiarity, i.e. hoisting using animated gifs ๐โจ. After reading this blog, you will be one step closer to becoming a Rockstar Developer ๐ธ๐.
Execution of JavaScript code.
Before understanding the concept of hoisting letโs first learn how javascript will run code behind the scene through an example.
var a = 2;
var b = 4;
var sum = a + b;
console.log(sum);
In this simple example, we initialize two variables, a and b, and store 2 and 4, respectively. Then we add the value of a and b and store it in the sum variable. Let's see how JavaScript will execute the code in the browser ๐ค.
When the JS first receives our code, it will create an execution context. Execution context will first scan through all the code, and allocate memory to all the variables and functions. For variables specifically var, JavaScript will store undefined in the memory, and for functions, it will keep the entire function code reference in memory.
Now that we have scanned and allocated memory to the code, we can execute the code. JavaScript will again start going through the whole code line by line.
As it encounters var a = 2, it assigns 2 to 'a' in memory. Until now, the value of 'a' was undefined.
Similarly, it does the same thing for the b variable. It assigns 4 to 'b'. Then it calculates and stores the value of the sum in memory which is 6. Now, in the last step, it prints the sum value in the console and then destroys the global execution context as our code is finished.
If you want to learn more about execution of code in JavaScript, I wrote an article about it on the dev community.
Link: https://dev.to/narottam04/how-javascript-works-visually-explained-269j
Hoisting in var
To understand hoisting in a variable defined with var, let's take the example we took in the execution of code. However, in this example, we're going to print the sum at the beginning, not at the end.
console.log(sum);
var a = 2;
var b = 4;
var sum = a + b;
The above code would throw an error in other programming languages, but not in JavaScript.
As we saw in the section on the execution of code, when JS first receives our code, it will create an execution context. Execution context will first scan through all the code, and allocate memory to all the variables and functions.
Now after scanning the code it will start the execution of the code line by line. In the first line, javascript checks the value of sum in memory and prints undefined as an output.
Now, as it encounters var a = 2, it assigns 2 to 'a' in memory. Until now, the value of 'a' was undefined.
Similarly, it does the same thing for the b variable. It assigns 4 to 'b'. Then it calculates and stores the value of the sum in memory which is 6.
If we console log after we calculate the sum letโs see the output.
Initially, it will print undefined, after computing the sum, it will print 6. This peculiarity in javaScript is called hoisting. Letโs learn hoisting in let, const keywords and functions.
Hoisting in let and const
When we refer to a variable declared with the var keyword before its declaration, it simply returns its default value: undefined! However, as demonstrated in the previous example, this can sometimes result in "unexpected" behavior.
Therefore, ES6 introduced two new JavaScript keywords to combat this unexpected behavior: let and const. These two keywords are Block Scope. To keep this article concise we would be learning more about the scope in another blog.
Letโs see the hoisting in let and const through an example.
console.log(a);
console.log(sum);
var a = 2;
let b = 4;
const c = 6;
const sum = a + b + c;
So letโs start with the execution of code again, when JS first receives our code, it will create an execution context. Execution context will first scan through all the code, and allocate memory to all the variables and functions. I know I've mentioned this multiple times, but it's important ๐ .
As opposed to var where undefined is stored in memory, let and const variables are uninitialized.
After scanning the code it will start executing the code line by line. The first line will print the value of โa' i.e. undefined in the console. But when we try to access a let and const keyword before initialization, a ReferenceError gets thrown whenever we try to access uninitialized variables. Uninitialized variables cannot be accessed since they are in a temporal dead zone.
Temporal dead zone simply means we don't have access to the variables before declaration.
If we try to print the value of โbโ instead of the sum the result would be the same.
The value of the sum must be printed once the deceleration of the value to the variable is complete.
Here is the visualization of the execution of code.
Hoisting in function
When javaScript begins scanning code and comes across a function, it stores the function's reference in memory. It is the reason why we can invoke the function before we have created it. Letโs see it in action through an example.
printSum(4,5)
function printSum(a,b){
const c = a + b;
console.log(c);
}
The output of the following code will be.
Let's try to visualize, first JavaScript will scan the code, it will encounter a function in the 3rd line, and it will add a reference of the function to the memory.
Javascript will now run the code line by line. It comes across a function on the first line. When a function is executed in javascript, it creates a new execution context within our global execution context. Consider the function execution context to be a mini execution context, with its memory and a location to execute the code in the function.
Read more about how functions execute code in this blog post.
Once all the code inside the function is executed, the execution context of the function is destroyed.
Reference
I hope this post was informative. ๐ช๐พ Feel free to comment or reach out to me if you have any questions.