To understand the execution context, you can treat it as a block of context that lets the compiler know about all the variables, functions declared in that context.
In simple words, the creation of execution context takes place in 2 phases-
- Memory creation phase
- Code execution phase
So, execution context can be said to set a context that first does all the fetching of variables & functions local to that context, and then does the code execution after that.
There is one more thing to be discussed which supports and maintains the order of the execution of execution contexts.
As for now, we will not cover the call stack in deep, but you can infer from its name, that it’s a stack.
Whenever the engine asked to create a new execution context, it first pushes the execution context reference into the call stack and pops it out when the code inside the context completes. Once the execution context pops out from the call stack, that execution context gets deleted completely, with no access to that context’s variables and functions defined. There is some slight variation in this in the concept of closures, but right now, we will ignore closure.
So what we infer is,
- Call stack manages the order of execution of the execution context.
- With each execution context creation and deletion, the JS engine pushes and pops out it from the call stack.
- Global execution context automatically becomes the first context to be pushed and the last context to be popped out from the call stack.
Let’s discuss the same with a simple code example,
So, in the above code snippet, it’s a simple JS code with some variables and functions defined and executed.
As we have already discussed in the beginning that the JS engine at the beginning of any JS code creates a global execution context and subsequent execution context as we execute further.
Let’s see how the above code executes with each execution context:
- At first, Global Execution Context to be created. This first gets pushed to the call stack.
- Memory Creation phase:
In the memory creation phase, all the local scope variables(in this, case the global scope), functions are recognized and thus given memory.
It will look something like this,
Variables are initially initialized undefined, while normal functions are given the complete function definition, the named function constants are initialised undefined, just like the variables are.
2. Code Execution phase:
Once the memory creation phase completes, we move over to the code execution phase. Here, the engine does the code running, value initialization, the function call, and all. Let’s discuss this phase for the above example.
- When the code executor reaches line 1, it sees
Here the engine asks the scope, do we have the memory declaration of the variable a. If it finds in the memory, it initializes the value 2 to a.
2. As it moves to line 2, it sees the function definition, and it moves further.
3. After this, it reaches line 7, where it sees value initialization of variable b.
It does the same behavior of asking the scope to find the memory declaration and initializes the value 5 to b.
4. Once it reaches line 8, here the engine sees a function call, and this leads to the creation of a new execution context local to the function called, and this context’s reference to being pushed into the call stack.
Now call stack contains two element — Global execution context and getTwiceTheNumber() execution context.
So, here we will discuss the new execution context created, with the call of function getTwiceTheNumber().
- Memory creation phase:
Here also, just the global context, all the local scope variables to the function will be fetched and initialized to undefined.
2. Code creation phase:
- So, now the engine reaches inside of the function and executes its first line of code. Here it sees —
let double = num;
It will ask the scope of the variable double is there in their local memory scope, if it exists the value num*2 (num=2) assigned to it.
2. Now, it will go to line 3, console.log the variable double.
3. In line 4, it seems that there is a return statement, which also signifies the end of the function scope. It returns the value to the function call line, and here,
- The execution context scope ends. It gets popped up from the call stack.
- The execution context scope gets deleted.
5. So, we are back again to line 9, which similarly calls the function getTwiceTheNumber(). Here new execution context to be created as we saw in the above section. It will be pushed into the call stack and popped when completed
6. Once line 9 has completed execution, there will be no line of code to process more, and here the global context code execution also completes, and it gets popped out from the call stack, leaving the call stack empty.
- The compiler does the variable & function memory creation.
- The Engine does the code execution.
- The scope sees the context of the variables exists or not.
This concept is the first step to understand further concepts like hoisting, closures, and all.
We will cover these concepts in some other story.
Do give a clap, if I was able to help you in any way to understand this concept.