Javascript Code Execution & Hoisting Explained for Beginners!
Prerequisite & Note
- Basics of Javascript
- The basic internal working of Javascript will be explained but note that the explanation is only for Javascript, other programming languages may do the same task differently.
- Making it clear again, you need no prior introduction to the internal working of Javascript.
Javascript is Weird
You might have heard that Javascript is one of the most hated yet popular programming language. There are multiple reasons contributing to the hate for Javascript and might differ for different people. The reason I call it weird is because of how mysteriously it works. Here are a few examples.
What can be done?
The solution to the problem of Javascript working weirdly is to understand how Javascript works under the hood! In this tutorial we are going to take one problem, and understand why that happens. Let’s get started!
The Problem..
I have created a simple application with index.html file & app.js file which outputs on screen, a simple Hello, World.
Consider the below code sample and try to notice the difference.
Notice the difference?
In the first code example, the function hello and favNum is initialised and then the function is called and favNum is logged directly.
In the second code example, the function hello and favNum are called before they’ve been initialised which should give an error, right? Let’s check!
Sample Code 1 gives the output we expect, but Sample Code 2 doesn’t. Umm… Hello from hello function is logged and favNum gives an undefined value? What is this!? Now begins the fun part, let’s keep going!
Hoisting
Hoisting is a mechanism where variables and function declarations are moved to the top of their scope before code execution
What does this mean in simple words? Taking example of our code, all the variables & functions created in our program move to the top of the file & the compiler understands the code, like our sample code 1. Have a look at the code below.
But… Functions give the desired output, why are we outputted undefined for favNum?
Undefined vs Not Defined(Reference Error)
It is very important to know the difference between undefined and Reference Error.
undefined basically means that the variable has been declared but hasn’t been assigned a value anywhere in the program whereas Reference Error means the variable hasn’t even been declared anywhere in the program so the program is unable to find a reference to that variable
Javascript Code Execution
To understand why we were getting undefined, we need to understand how Javascript works beneath the hood- how Javascript assigns value to it’s variables, does it go line by line and execute all the lines or goes to a specific method and execute it first? To answer all these questions, let’s understand what an Execution Context is!
Execution Context
In JavaScript, execution context is an abstract concept that holds information about the environment within which the current code is being executed.
In simple words, you can imagine Execution Context as a Big Container that holds all the information about your code & Javascript executes the code in an Execution Context.
Assignment of Variables
We know that all the code execution in Javascript takes place in an Execution Context. Therefore, the assignment of variables takes place in the Execution Context.
Execution Context has 2 components, one component is called Variable Environment or simply Memory Component and the second is Thread of Execution or simply Code Component.
As you can guess by the name itself, Variable Environment stores all the variables in the memory in key-value pair whereas Thread of Execution goes through the program line by line and creates a Stack which can be called Call Stack! Below, is a diagram representing the same.
Now let’s understand what will happen taking in mind our code sample 1 first, then we will get the answer of code sample 2.
So, what Javascript does is goes line by line, stores every piece of line in Thread of Execution. Take for example, the below piece of code:
console.log("Waddup");
Internal Working (Code Sample 1)
Given below is the Code Sample 1 which we will be referring to:
var favNum = 10;function hello() { console.log("Hello");}
hello();console.log(favNum);
So what happens is, the compiler goes through line by line, stores the code in Thread of Execution and whenever sees an assignment of variable or declaration of function, stores it in Variable Environment in key-value pair.
The most amusing part of how it stores variable in Variable Environment is that it doesn’t directly store the value of the variable favNum as 10. While going through the program line by line, saves favNum as undefined in Variable Environment and when running the Thread of Execution(remember, when the variable gets assigned the value of undefined, it also stores the code in Call Stack/Thread of Execution) saves favNum as 10. Below is depiction of how key-value pair of a variable is stored in Variable Environment.
key : value
favNum: undefined
// runs the Call Stack or Thread of Execution or Thread Stack
favNum: 10
Function Assignment
We understood how variable is assigned the value. But, how does function get it’s value, does it also get undefined as value first and then the code inside it takes place? Nope! That’s not how Javascript works!
Functions in Javascript can be considered as a mini program in a program itself. What do I mean by that? Let’s understand it with the help of our Execution Context Diagram.
Basically, a whole new Execution Context is created inside the Global Execution Context and whatever variables are created in the function are then added to Variable Environment. The Execution Context works the same way as Global Execution Context. This would also explain Scoping in Javascript. Hence, Functions are considered as mini programs. If there is a function inside a function, one more Execution Context will be created but inside the Execution Context which is inside the Global Execution Context.
Code Walkthrough of Code Sample 1
Now, that we know how variables and functions get assigned. We can understand Code Sample 1.
Step 1: Filling The Thread of Execution and assigning undefined to variables
Step 2: Clearing The Stack- Variable
Step 3: Clearing The Stack- Function
When the Thread of Execution is clear i.e. it is empty, it means that the whole program is complete.
We have understood the internal working of Javascript and we are technically over with the explanation, but as a bonus I’d like to share with you the proof & actually how I confirmed that whatever I was learning was correct.
Proof Of Variable & Function Assignment (Bonus)
Head on over to your favourite code Editor and create a Basic Web program like the one I created (with just HTML, CSS). Launch the website in Chrome & Open Chrome Dev Tools (Inspect Element).
On Windows: CTR+SHIFT+I
Head on over to Sources Tab and click on the script file you had created. For me, it is app.js. Add a debugger on the first line. Check the image below and see if you’re with me.
The next thing to do is to reload the page, so that the debugger starts and we see the results!
CTR+R
You should see results like this. Open the Scope on the right hand side and expand Global. There, try to find favNum variable.
See, the variable, favNum is undefined and if we minimise the Global variable list again and see the Call Stack, we will see an “anonymous” tag. Hence, we can prove the things you’ve learnt today!
Let’s do the same for the function hello. Click on next icon in debugger to move forward to the next line. Below is the image that shows how function hello is saved in the memory.
This proves the way we thought how functions are stored in the memory.
Let’s move on to the next line where we see the variables getting the values we set. (I had put 2 breakpoints, one on the hello function and other on favNum variable, so I have them in a different section. If you haven’t done so, find the variables in memory as you did earlier).
Moving to the end of the program, you should see the debugger getting closed automatically & call stack completely empty.
Here we go! You’ve completed the basic understanding of Hoisting, Internal Working of Javascript which is the most important thing while understanding these kind of topics but there is more left to be understood. For example, let vs const. If you understood well and wanna learn more, let me know about it in comment section, I’ll make one on it!
Thank You For Reading!