As JavaScript is getting more and more popular and extensively used, people are utilising its ability to make both server and client-side applications which are fast and use fewer resources of a machine.
In this post, we will talk about the JS Engine, its Execution context its phases and how things work inside of a Javascript Engine.
Before going into the terminologies let's see why getting the deep info about the internal workings of JS is more important. If we talk about Javascript, without understanding the internal working of the javascript engine and runtime we might get output from the program which we did not expect to come. This makes javascript stand out from other languages and harder to understand for some programmers.
Now let's get started with an introduction to the Javascript Engine.
What is a Javascript Engine?
"Javascript Engine is nothing but a program that takes code written in javascript and executes it."
Internally, several steps are involved while executing the code, but here we just need to know that, the javascript engine takes high-level code, converts it into machine code, does memory allocation, and executes our program.
Currently, there are several Javascript engines out there in the internet space. but most importantly all the engines follow a standard which is known as "ECMA Script". ECMA Script tells every engine implementation how the Javascript will be executed or we can say how will it behave that's why with every engine Javascript almost works in the same way.
The most popular JS Engines are Google V8 engine, Spider Monkey, Javascript Core, Chakra etc.
Where is this Javascript Engine reside?
Now you are wondering, Ok I got the point what does JS Engine do but where is this JS engine reside? Is this engine came builtin inside my machine or is it situated somewhere else?
So here is the answer to that question. The Javascript engine is placed inside the JS runtime ( we will cover JS runtime in the next part ) and this JS Runtime is prebuilt inside our browser. So when we install any browser we will get this Javascript Engine embedded inside it and with the help of this engine, the browser can handle all the websites/web apps that use javascript.
Components of JS Engine:
Now here starts the main part of JS Engine, the JS Engine has two major components,
Memory Heap & Call Stack.
1. Memory Heap: This is the place where all the primitives i.e. functions and objects assigned memory space.
2. Call Stack: As the name suggests this is a data structure which is a stack and is maintained by the Javascript Engine to keep track of the execution flow of our javascript program. Call stack keeps an eye on execution context which we will talk about just in a bit. Whenever a function is invoked a new Execution Context is created and Javascript Engine pushes that execution context inside this stack data structure. As we know stack uses LIFO i.e. Last In First Out property so whenever an execution context finishes its execution it just gets popped out of the stack.
How code is Internally executed?
Now as we know that javascript code is executed by the JS Engine but how it is executed let's get into that.
When we execute a particular piece of code it goes inside the Javascript Engine it creates a special environment to execute the code which is known as "Execution Context".
Now the question is when exactly this execution context is created? So to understand this we need to know how many types of execution contexts are there.
So, majorly there are two types of the execution context. first is the GEC i.e. Global Execution Context and the second one is the FEC i.e. Function Execution Context.
Now, I know you might be overwhelmed with all this info but just bear with me all info will make sense in just a bit.
Global Execution Context:
The Global Execution Context is created every time whenever your script file gets loaded inside the Javascript Engine in other words whenever you try to execute a javascript file, first of all, the global execution context is created. Inside that, all of the javascript code get's executed. For every javascript file, there will be only one GEC.
Function Execution context:
As the name suggests, the function execution context is created whenever you try to call/invoke a function. As the function execution context is created separately for every function, there can be multiple FEC exist while the code is being executed.
Both the execution context GEC and FEC are similar just there is only one difference which is the time of creation.
Now, further, this execution context is divided into two parts:
Memory & Thread of Execution / Code
1. Memory: This is the place where all the primitives i.e. functions and objects assigned memory space.
2. Code: The thread of execution or we can say the Code part is the place where our program actually gets executed.
Both these parts of the execution context have their own phases to process the program.
Memory Creation Phase
Code Execution Phase
In the Memory Creation Phase, all the variables and functions get stored inside our heap i.e. memory area. All the variables and functions are allocated with memory "at once" i.e. the memory allocation starts from the top and goes till the script ends. Initially, the variables are assigned with a default value of "undefined" and the functions are stored as it is written in the code. because of this process, a concept called Hoisting takes place. also, the creation phase adds one more piece to the memory which is known as the "Lexical Environment" but we will not talk about lexical environment and hoisting here we will save it for later.
In the Code Execution Phase, The variables which have been assigned undefined earlier in the creation phase are now assigned with the actual value defined inside the program and if we talk about function if they are invoked i.e called somewhere in the program it will create its own Function Execution Context inside its parent execution context which by default is GEC i.e global execution context.
Now let's understand all things we just saw with an example of how our code gets executed. Let's say we have a function which takes a number and returns the square of the given number.
var number = 2;
function getSquare(num) {
var answer = num * num;
return answer;
}
var square2 = getSquare(number);
var square4 = getSquare(4);
Now as the first step Javascript Engine will create a global execution context and as we discussed earlier in this section. This execution context will be pushed inside the Call Stack to keep track of the execution flow.
Now our first phase starts ie. the “creation phase”. Here in our program, we can see there are three variables at the first level (outside any function) number
, square2
and square4
. All these three variables are allocated with memory in our memory space and assigned with undefined
. The function getSquare()
is stored as it is in our memory area.
After the memory allocation phase-2, i.e actual code execution starts line by line. As the execution starts it will encounter 1st line which is
var number = 2;
Now as the variable number is already stored inside the memory Javascript Engine will just allocate its value which is 2
to it and update inside the memory area as well.
Now, our code execution will come to the function definition but here nothing will happen because the function is already assigned memory in the creation phase. Remember the function code only gets executed only when it is invoked/called.
Now it will encounter the below line:
var square2 = getSquare(2)
Here we are invoking a function by passing an argument and storing its return value to the square2 variable.
As we are invoking the function a new Function Execution Context is created inside our Global Execution context (GEC) which has its own memory, and code execution part and also, our new execution context is pushed inside the call stack.
Now as the stack have the Last In First Out property our new execution context will start executing first.
function getSquare(num) {
var answer = num * num;
return answer;
}
For this new execution context, the creation phase will start. We can see here there are two variables that need memory to get stored which are answer and num. In the function execution context if we have some parameters then those parameters are also treated as variables and assigned memory just like this.
Now, after phase one again phase two i.e. the code execution phase will start.
It will encounter the first line of the function “function getSquare(num)”
. The value which we have passed while invoking the function is assigned to the num so the num will be assigned 2.
Now line 2 which is var answer = num*num;
will start executing. The result of num*num which is stored inside the answer
At last return statement will get executed. As all the code is executed the function execution context is popped off from the call stack and the value which we have returned is assigned to square2. Return statement tells a function execution to jump on the previous execution context so it will again start executing GEC.
Now, this same process is followed for square4 and it is assigned with 16. After execution of both the function call, our whole program ends and the global execution context is also popped off from the call stack and memory gets cleaned up.
This is how the javascript program executes under the hood.
As the Javascript Engine executes one line of code at a time that is why the javascript is "single-threaded".
Tip:
You can see all these things working live inside your browser
Conclusion:
JavaScript Engine and Execution Context is the basis for understanding many other fundamental concepts correctly.
The Execution Context (GEC and FEC), and the call stack are the processes that are carried out internally by the JS Engine to run our javascript code.
Hope now you have a better understanding of what is JS Engine and in which order a program/code run and how JavaScript Engine allocated memory to them.
If you like the content please share it with your friends, colleagues, coding-buddies.
Have a nice day
Top comments (0)