How does JavaScript work — Execution Contexts and Call Stacks
JavaScript has become one of the most popular programming languages of all time. It is also leading the pack in terms of GitHub repositories and is the most discussed programming language on Stack Overflow.
It thus becomes very important to get your basics clear and know what happens behind any JS program and how is it executed, if you want to dive deep into JS.
So here we go!
Execution Context (EC)
Everything in JS happens inside an Execution Context. It is the environment in which JS code is executed. It consists of the value of ‘this’ , variables, objects and functions that a JS code has access to at any given instant in the form of key-value pairs. Every block of code will have its own EC in which it is executing.
An Execution Context and be thought of as a block shown in the above image. It consists of two parts —
- Memory- All the variables present in your code are stored here in key-value pairs
- Code- This is a thread where code is executed, one line at a time
Types of Execution Contexts ?
There are 2 types of ECs:
- Global Execution Context (GEC) : This is created once for every program by default. It consists of the code that is not inside any function. The GEC is mainly responsible for two things :- it creates a global object which is the window object (for browsers) and it sets the value of ‘this’ to equal to the global object. The GEC gets destroyed once the execution of the whole program is over.
- Function Execution Context (FEC) : Every time a function is invoked, an execution context is created for that function. This gets destroyed once the function returns something or its execution is over.
How is an Execution Context created ?
JavaScript Engine creates the Execution Context in two stages :
- Creation Phase
- Execution Phase
Creation Phase: In this phase , the JS engine just scans the whole code but does not execute it. It creates the scope chain and allocates memory to every variable (with its value being undefined) and functions within its scope. After this, it also initializes the value of ‘this’.
Execution Phase: In this phase, the JS engine again scans the code to update the variables and complete the execution.
Whenever you run your JS code, in the Creation Phase, a Global Execution Context is created which stores all the global variables with a value of undefined and functions with its body as the value. Thereafter, a unique EC is created for every other function (FEC) which works the same way — it first stores and allocates memory to all the variables local to that function, executes the block of code and destroys itself after its Execution Phase gets over.
Example:
Based on the above image, let us see what happens when this code is executed:
- When this program runs, it first gets into the Creation Phase. The code is scanned by the JS engine and the Global Execution Context is created.
2. On the second scan, when it enters the Execution Phase, the whole program is executed, one line at a time from top to bottom - because JavaScript is a synchronous, single-threaded language. So, as per line 1, a’s value will update to 10.
3. Then the control will go to line 8, var result = doubleTheNumber(a)
. This calls the function and passes the value of a
i.e. 10 as the parameter. The control then goes to the function.
4. Now, for the execution of this function block, similar steps of executing JS code would be followed. An execution context would be created specific to it. In the creation phase, memory will be created for doubledNumber
.
5. In the execution phase of this function, since the value of number
is 10, doubledNumber
would be 2*10 i.e. 20.
6. After the return statement, the execution context for that function will be destroyed and control goes back to var result = doubleTheNumber(a)
, where result’s value will be updated to 20.
7. The last line will be executed after which this Global Execution Context will also get destroyed.
This example is mainly to help you understand how JS programs are executed. But how does the Execution Context actually look and how is everything managed?
Well, this is done by CALL STACK.
CALL STACK
Call Stack maintains the order of execution of Execution Contexts. It is also called by names like Program Stack, Control Stack, Runtime Stack etc.
It is a stack that consists of all the ECs. The GEC is always the first EC to be pushed into this stack and also the last one to get popped out. Whenever a new EC is created, it is pushed into the call stack. When its execution gets over or it returns some value, it gets popped out and the control goes to the one below it in the Call Stack.
Example :
As per this code ,
the Call Stack operations would be like this:
Explanation:
When the code is executed, the Global Execution Context will be created first and pushed into the stack. During the execution, when the control goes to function, a new Execution Context would be created specific to it and pushed into the stack. Once its execution gets over, this EC would be popped out and the control will go back to the GEC. After the complete execution of this code, this GEC will also get popped out!
Similarly, you can also practically check the call stack operations for any given JS code.
Open your JS code in the browser -> Open console -> Go to Sources . You will get the call stack like this in the image below:
At this moment, the call stack should be empty because the code has fully completed its execution. To see the creation and deletion of ECs, add breakpoints to your code. Add a breakpoint before the first line and check the Call Stack status. You will see the Global Execution Context pushed to the stack. Similarly, add breakpoints on other lines and check the call stack for yourself.
Thank you for spending your valuable time reading this blog :)
I hope you enjoyed and gained knowledge through this. Make sure to leave your thoughts in the comments section!
Happy Coding :)