Under the cover of JAVASCRIPT ❤️- (Part: 2)
This article will cover how does the execution context works, what are the global and the functional execution contexts, window object and call stack?
Earlier, we had understood how javascript is different from other languages and versatile to work on both the client side and the server side. Also, the execution context was introduced and its components too. If you are relatively new to this, you can check out my previous article where it has been explained from the scratch.
A walk-through 🚶of an Execution Context with a piece of code
An execution context is a container/space created by the javascript engine to execute the code as soon as it is ready to be executed in the runtime environment. This execution context has two components: memory creation/variable environment and code component/thread of execution. It's time to see these two phases in detail with an example.
First, let's start by running an empty javascript file.
What are global execution context and functional execution context 🤔?
The javascript engine skims through this empty javascript file. Take notice that it creates the global execution context. What is it?
An execution context has an environment created to execute the code which we have repeatedly mentioned. The execution context in terms of the global word is simply referring to the global scope in javascript. That means, the global execution context is a base execution context and by default stores variables that are only outside the function and present in the global scope.
Just observe the below image. On the left-hand side, we have our whole code snippet and on the right-hand side, it is a memory creation phase in the global execution context. The javascript engine has allocated memory to all the variables in the global scope but not to the variable res. It is because var res is in the functional scope and not in the global scope.
Do you wonder, why that happened?
- This is where the functional execution context shows its swag. It will have a separate room/space comprising the memory creation and the code component phases.
- The functions in themselves are a little program in this whole program, so the code inside that function gets executed in a separate environment, i.e. functional context. Don't worry! If not now, it will become more clear later. Hence, there are two types of execution context: first, global/default and second, local/functional.
What is the window 🪟object and the 'this' keyword referring to?
When the javascript engine creates the global execution context, it gives the global object 'window' and the "this" keyword by default in the global memory. Note that 'window', the global object is in the context of the browser javascript runtime environment that we are talking about. In node.js the global object is 'global' but we will not go into its detail here.
Assume the window object as a gift that has many methods and properties like click, media player alert, etc inside it which are accessible to you. You can also explore those on the MDN. Take a simple example, just go on the console and write:
window.alert('Hi window alert');
It will send you an alert.
Whatever we write between the script tags or inside the javascript file, becomes the methods/properties of the window object. Let's try it too.
Write this code on the console
var a = "AwesomeJS";
, hit enter and then writewindow
, hit enter. It will show the window object and after opening it, you will now find that it has the variable 'a' stored inside it.But also make a note that, the window object has no access to the variables inside the functions stored in the global context. Instead, you can call the function through the window object. The below code will make it easier to understand.
Even if we don't write anything and run the javascript file, the window object and this keyword will be by default created in the memory phase of the global execution context.
What about the 'this' keyword?
this: window
seems to tell that the 'window' object and 'this' keyword are both the same. Well, that's true, 'this' is always pointing to the window object only and it does have something to do with which we will discuss in some other article. We're enough of THIS here (pun 😂 intended).
Step-by-step 👣 code execution by the javascript engine
var num1 = 4;
var num2 = 7;
function multiply(x, y){
var res = x*y;
return res;
};
var resultOne = multiply(num1, num2);
var resultTwo = multiply(7, 7);
console.log(resultOne, resultTwo);
The javascript engine will create the global execution context as soon as it has this code ready to execute. From here, the work starts in the global execution context.
Creation 🧠 phase:
It creates the global object 'window' and the 'this' object is attached to the global object.
It creates the memory for storing variable declarations and function declarations.
It assigns the 'undefined' placeholder value to the variables and defines the function's reference. Just like below, you see.
Execution 🧵 phase:
It starts executing the code line by line now from top to bottom.
As soon as it comes across the variable, it will assign the variable the actual value which had an earlier 'undefined' placeholder. The first line would be variable num1.
num1: undefined --> num1: 4
- It will go to the second line of variable num2 to execute now.
num2: undefined --> num2: 7
Then it comes across the next line of function multiply, it will just skip this since this function was already defined in the global memory.
Next, it will jump to the line of variable resultOne
var resultOne = multiply(num1, num2);
which will invoke the function multiply() and from here, a new functional execution context will be created separately to execute the multiply function. Remember? the functional execution context has Swag.We can see the image of all these steps executed so far.
function multiply(x, y) { var res = x*y; return res; };
This functional execution context will also have the same phases. So, in the memory creation phase, the javascript engine allocates the memory to
x
andy
,res
and stores them with an 'undefined' value.Now the javascript engine starts executing the code line by line in the execution phase. In the functional execution context, we get access to a special value which is the arguments object
arguments: { 0: 4, 1: 7, length: 2 }
. It references all parametersmultiply(num1, num2)
and the values 4 and 7 will be passed into this function.Thereafter, the calculation part will take place and the calculated value '28' will be now assigned to the variable
res
which was before 'undefined'.Later, this function will return the value and as soon as it is returned, this whole functional execution context will get destroyed.
The returned value was assigned to the resultOne which had invoked this multiply function.
var resultOne = multiply(num1, num2);
var resultTwo = multiply(7, 7);
The javascript engine will continue the execution phase in the global execution context and go on resultTwo, line of code now. How it will do? Pretty simple! just like it executed resultOne by creating a function execution context.
After resultTwo also gets the returned value the same way. Then, the whole global execution context will be destroyed. Because now we have nothing to execute, right? Now, we can refer to the remaining steps also in the below image.
So, we have summed up this whole demonstration that how the javascript engine works on the code execution.
What is a call stack 📚 and why is it there?
Have you ever seen something stacked? Well, you might not have heard this term before, but I can say that you must have seen things stacked up or we can say piled up. A perfect example would be books stacked up.
Now, instead of books, imagine all the execution contexts stacked up in the sequence we created. Want to see what the call stack looks like? Let's go and check.
When the javascript engine moves to execute the code, it creates a global execution context at the base.
The global execution context enters the creation phase and moves to the execution phase. The JavaScript engine executes the call to the multiply(x, y) function invoked at the line
var resultOne = multiply(num1, num2);
and creates a function execution context for it by pushing it on top of the call stack.JavaScript engine executes the
multiply()
function and pops it off the call stack. And we end up with the base global execution context only.The javascript line will execute the next line of code
var resultTwo = multiply(7, 7);
in the global execution context now. Again on the function call, the functional execution context will be created and pushed on top of the call stack.After the code is executed inside the functional execution context, it will pop off.
Now there is nothing to execute in the global execution context also. So, the execution phase ends and it also gets popped off and the call stack will be empty.
You can see the whole six steps marked in the image below. Also, the pattern of data structure that the call stack follows can be seen in it. It is based on LIFO (last-in-first-out) which means when you go to a wedding and see the dinner plates 🍽 stacked up, you pick from the top of that stack and not from the bottom, right?
So, this is how the call stack pops off from the top and works like that.
Yes, you got it right call stack is the single-threaded stack of executions contexts piled up. But the question is why do we have it? It is really helpful in maintaining the tracks of execution contexts as you see these get created and destroyed. Also, it becomes super handy when we have multiple functional contexts in case a function is called within a function, which might start looking complex and give unnecessary nightmares.
Before wrapping this call stack, we should also know to not get confused later that a call stack is called by various names such as Execution context Stack, Program stack, Control stack, Runtime stack, Machine stack, etc.
Conclusion 🌟
In this second part, we had an in-depth understanding of how the javascript engine executes the code under the cover. We got a good grasp of the types of the execution context followed by the call stack steps, but there is one more eval function execution context that I have not mentioned since it is not really recommended to consider in the use case.
I hope you found this article helpful enough to feel this lovely javascript ❤️ and if you have any feedback for me, please feel free to share it. Till then, Happy coding with functional context's SWAG 😎! Thanks for reading, Stay tuned for the next article!😁