Want to understand "How Hoisting ποΈworks in JavaScript β€οΈ"?
This article will make "Hoisting" a π° piece of cake for you.
What is Hoisting ποΈ?
In simple words, Hoisting is just the mechanism that allows us to use the functions, variables or classes before we have declared them in our code. We will understand this single line with a bunch of examples.
Before we start, I assume you know how the execution context works. If you don't know and have not followed my previous article which explains the execution context in detail, then trust me to check it out and it will be much easier for you.
How does hoisting work π€?
1. Hoisting of 'var'
With no more wait my dear friend, let's get started with a code snippet that how will it run and what gets printed when we try to access "myname".
console.log(myname);
var myname = "Riya";
console.log(myname);
At first, the javascript engine skims through the piece of code and it comes to line 2, var myname = "Riya"; to create a global execution context and in its memory allocation, it gives the variable myname an undefined special placeholder value.
Followed by the completion of the memory creation phase, now the execution phase starts from line 1.
The javascript engine will bring the undefined value which was initialized since it hasn't yet come to line 2 where myname has its original value.
But why it printed the output undefined and didn't throw any error and we could still access the variable from line 1 before we declared it on line 2? Because this is where the hoisting takes place under the hood of it.
This simple process hoisting is just that the javascript engine assumes beforehand that the variable hoisted to the top of its scope is yet to be assigned its actual value later. So, it returns undefined for the variable it had stored some space with this special placeholder at line 1 of the code.
Here, we are completed with the hoisting of var. However, let's see the execution further while the value assignment takes place.
After moving to line 2 of the code, now the Javascript engine assigns "Riya" to the variable that had an undefined initialized value. So, the var myname is no more
undefinedbut has the value "Riya".Now on the last line, what value do you think it will print?
Yes, you got right. It prints "Riya" since at the previous line only, the value was initialized or assigned as "Riya" and so we got the expected output "Riya".
2. Hoisting of 'let' and 'const'
console.log(language);
let language = "JavaScript";
If we run this code, let's see below what will be the output.
We get this error message: ReferenceError: Cannot access 'myname' before initialization. Your question might be: Don't they both "let" and "const" get hoisted?
Yes, let and const do get hoisted and the behavior of hoisting is not similar to the case of var we saw.
Do you notice that, unlike var, the scope of language is in something like script but not global and it is initialized with the value of undefined there, right? This means let and const also get hoisted. I am sure by now you are curious about which place is this where "let" is sitting.
It is a temporal dead zone, my dear friend. I know it sounds like intimidating jargon. haha!
This zone is simply pointing to the time when language was hoisted there with an undefined value until it is assigned its original value. So, till this point of time, this temporal dead zone is not accessible.
Otherwise, if the "temporal dead zone" had been accessible, we would have got "undefined" printed for the first line before we declared it on the second line of the code.
So, yes, this is what makes hoisting different in let and const. And, you can also consider this example for const as well.
3. Hoisting of functions
3.1 Hoisting of "function declaration"
sendDadjokes();
function sendDadjokes() {
"Riya says: Is Understanding JavaScript a piece of cake because it has a slice? haha!"
}
Well, jokes apart! π let's pay attention to the code now.
Here, we are focusing on a function declaration, to create it, we just need to follow a basic syntax:
function functionName(parameters) { statements}
Now, let's take a look at the output of the code.
Notice we tried to invoke the function on the first line and we could access the function fully, also, it didn't return undefined but the actual line that we wanted to get printed. Of course, we must already know how it works.
The JavaScript engine allocates the memory to the function declaration fully as it is. So, when the execution starts, as soon as the function is invoked, it will get the value returned by the function execution context's code component phase and later, that function execution context gets destroyed.
Because the JavaScript engine completely hoists the function declaration. It is not like the case of variables that are partially hoisted as undefined.
If you find it not easier to understand here. You can check out my previous article, where I explained how the function execution context works.
3.2 Hoisting of "function expression, anonymous function and arrow function"
Function declaration was also a type of function only but my dear friend, there is no chance of hoisting in the case of function expression, anonymous function and arrow function. So, first, let's see the syntax of a function expression.
A function expression is simply nothing just a function declaration that is stored in a variable. The below code just explains this simple line.
a();
const a = function sendDadjokes() {
console.log("Riya says: Is JavaScript a piece of cake because it has a slice(); ? π);
}
Before we see the output, let's analyze it from our heart. What do you see "const a" as?
Come on! It's time to hack the mind of the JavaScript engine. π
The JavaScript engine while allocating the memory at first only knows "a" as a const variable and it will be in a temporal dead zone with the value undefined, right? Because it doesn't know "a" is a function, it will just treat it like a constant variable.
So when it goes at the first line during the execution phase a(). It is not possible to access "a" because "a" is in a temporal dead zone. Therefore, it will return an error.
Next is now anonymous function. It is just like a function expression. However, if we look at the below syntax, the anonymous function term is self-explanatory in that we don't need to name this function and it is useful while assigning them as a value.
const sendDadjokes = function () {
console.log("Riya says: Is JavaScript a piece of cake because it has a slice? haha!");
}
sendDadjokes();
- The arrow function syntax is something like this, check below and it will be also treated the same way we discussed.
sendDadjokes();
const sendDadjokes = () => {
console.log("Riya says: Is JavaScript a piece of cake because it has a slice? haha!");
}
So, we have also completely understood how the function declarations get hoisted and how the function expression, anonymous function and arrow function never get hoisted.
Note, if you try the above two code snippets by using "var" instead of "const", there will be no temporal dead zone. However, it will still throw an error which is a type error mentioning "a" is not defined. Just check out why it will throw this type of error comment. If you have any doubts, drop that too in the comment section.
Conclusion π
I hope you found this article helpful on hoisting with some examples and some fun since the javascript makes us turn our head to it then why not make it more interesting, right? haha! Let's learn together and hack the JavaScript engine's mind when someone next time asks us: Tell me how hoisting works. It will be just a piece of cake. Let's have it! π
If you feel confident enough after reading this article, I would be happy and if not, do you have any feedback for improvements? I would be happy to hear those. Until then Happy coding. Stay tuned, see you in the next article. π