Execution Context
Everything in JavaScript happens inside the Execution Context, It can be assumed as a container inside which the whole JavaScript program runs.
What is an Execution Context ?
Execution Context is like a big box and it has two components in it.
The first component is also known as the memory component. This is the place where all the variables and functions are stored as a key value pairs, as shown in the diagram. Memory Component is also known as Variable Environment.
The second component of this execution context is the Code Component, this is the place where code is executed one line at a time. It is also known as Thread of Execution. This is just like a thread in which the whole code is executed one line at a time.
Hence we can say that, JavaScript is a synchronous single threaded language.
What is meant by synchronous single threaded language ?
Single Threaded means Javascript can only execute one command at a time, and synchronous single threaded, means that JavaScript can only execute one command at a time and in a specific order that means that it can only go to the next line once the current line has been finished executing.
What happens when a program in JavaScript is executed ?
When any program is executed, a Global Execution Context is created. Now, let’s see how this Execution Context is created with the help of a JavaScript program.
var N = 2;
function square (num){
var ans = num * num;
return ans;
}
var square2 = square(N);
var square4 = square(4);
In this program, we have this variable N
, which is 2, and there is a function square
, which returns the square of a number which is being passed inside this function, and there are two other variables, square2
and square4
, which are just invoking this already created function.
So when this whole code is run, a global execution context is created. This execution context is created in two phases. The first phase is known as the Memory Creation Phase. In the first phase i.e memory creation, JavaScript will allocate memory to all the variables and functions.
How does it allocates memory ?
As soon as JavaScript Engine encounters this line 1, so it allocates the memory to N
by reserving space as shown in diagram below. Now the JavaScript Engine goes to the line 2, it sees that there is a function, which is named a square
, so it will allocate the memory to function square
. Similarly it will allocate memory to square2
and square4
.
How does the JavaScript Engine stores values ?
When the engine encounters a variable, it stores it and gives it a special value which is
undefined
. For functions, the engine store the whole function code as it is in the memory component of the execution context.
Now, the program will go in phase 2 which is Code Execution Phase. Now for this phase, JavaScript Engine once again runs through this whole JavaScript program line by line, and it executes the code now. This is the point when all these functions are executed and every calculation in the program is done.
Now when the engine encounters this first line, where N
is equals to 2, it actually places the 2 inside the N
. Till now, the value of N
was undefined but now in the second phase of creation of execution context the value of N
in the memory component is getting updated to 2. Now the engine will go to line 2 and it’ll encounter a function so it has to do nothing from line 2 to line 5. When it moves to the line six, here it invokes the function square
.
Functions in JavaScript are like mini programs, so whenever a function is invoked, it acts like a mini program and a new execution context for that function is created. All of this is happening inside the Global Execution Context.
So here, a new execution context inside this is created and this execution context again has two components, which are memory component and code component. Now we will again go through the creation of this execution context and there will again be two phases involved.
In the phase 1, first of all, the memory is allocated to variables and functions inside this function. We will be allocating memory to num
and ans
as undefined. Now in the phase 2, we will be executing the code line by line. In the execution of this code, the value of num
will be equal to the argument passed during the invocation of the function which is N
and the value of N
is 2. So the value of num
will be 2. Then in line 3, it’ll calculate the result and will update the value of ans
which will be 4. Then the control will go to the next line which has a return
statement. This means that the value of ans
will be returned and stored in the variable which is equal to the invocation of this function call, square2
in this case. So the value of square2
will become 4 and the execution context made for the function call will be deleted.
The same process will be done for the next function call for the square4
as well. So our Global Execution Context
will look like this.
In this way, a program can be executed in JavaScript.
Call Stack
In a JavaScript program, there is a lot going on here and there, how does the JavaScript Engine manages all of this, which function to execute which execution context to create and which to delete ?
All of this is done by maintaining a Call Stack. Every Program in JavaScript has its own call stack
What is a Call Stack ?
It is a stack and every time in the bottom of the stack, we have our Global Execution Context. That means whenever any JavaScript program is executed, this call stack is populated with this Global Execution Context. This whole execution context is pushed inside that this stack and whenever a function is invoked or a new execution context is created as in case here in line number six, we created this execution context, so this execution context is pushed inside the stack. When a function is exited or returns any value then its execution context is popped from the call stack. Finally when the whole code is executed, the call stack gets empty and indicated the completion of the program.
It is known by many names like
Call Stack
Program Stack
Control Stack
Execution Context Stack
Runtime Stack
Machine Stack
Hoisting
Let us see an example where we have a var and a function.
var x = 7;
function getName(){
console.log("Hello")
}
getName();
console.log(x);
So on running this program the output will be
Hello
7
But if we change the order of these lines of the code like this
getName();
console.log(x);
var x = 7;
function getName(){
console.log("Hello")
}
So, on running the program above it’ll throw error in most of the programming languages but in JavaScript, it will give this following output
Hello
undefined
Here it gives undefined for the value of X
but the function got executed successfully. This is happening because of execution context as the X
has been given some memory space in memory creation phase so while the code execution phase the value undefined is printed.
This phenomenon in JavaScript is known as Hoisting
Hoisting in JavaScript is a way by which you can access these variables and functions even before you have initialized them or you have put some value in them.
Now let’s suppose instead of the function we make an arrow function like this
getName();
var getName = () => {
console.log("Hello")
}
So, on executing this program, it throws an error like this which says that getName is not a function. This is because in an arrow function we declare the functions as variable and when we are accessing it before its initialization, it acts as a variable only and its value is undefined.
So, the functions are copied as it is in memory component of execution context only when they are pure functions.
This all is possible because of Hoisting only.