Table of contents
- The Script: A Story in Code
- Act 1: Setting the Stage - Global Execution Context
- Act 2: Drama within Functions
- The entire flow:
- The Harmony of the Call Stack
- Key Takeaways
Have you ever wondered what exactly happens when you hit that "Run" button in your JavaScript program?
Let's take a journey through the intricacies of JavaScript execution and uncover the magic that takes place behind the scenes.
The Script: A Story in Code
function calculateSum(a, b) {
let result = a + b;
function multiplyByTwo(num) {
return num * 2;
}
let doubleResult = multiplyByTwo(result);
return doubleResult;
}
let finalResult = calculateSum(3, 5);
Act 1: Setting the Stage - Global Execution Context
When we run the above code a global execution context is created.
The execution context is created in two phases.
1st phase is the memory creation phase.
2nd phase is the code execution phase.
Memory Creation Phase
With the above code, in the first phase, javascript will allocate memory to all the variables and functions.
The
calculateSum
function takes its place in the memory spotlight. In case of functions, it will store the entire function code in the memory space.finalResult
is allocated memory with the valueundefined
in the first phase.In the bottom of the stack we have our global execution context that means, whenever any JS program is run, this call stack is populated with this Global execution context. This whole execution context is pushed inside this stack.
Code Execution Phase
Now javascript once again runs through the whole javascript code line by line.
From line 1 to 11, there is nothing to execute
The
calculateSum
function steps into the spotlight. Functions are like mini programs. When a function in invoked a new execution context gets created.On line 13, a new execution context gets created with two components. Now, again we will go through two phases which are memory allocation and code execution.
This execution context is only restricted with function code which is from line 1 to 11.
Whenever a function is invoked, or a new execution context is created, that new execution context is put inside the stack which is
EC1
here.
Act 2: Drama within Functions
Memory Creation Phase (calculateSum
Execution Context)
Now we dive into a subplot:
Memory allocates space for
a
,b
,result
anddoubleResult
asundefined
.The inner function
multiplyByTwo
is also added in the memory with the entire function code.
Code Execution Phase (calculateSum
Execution Context)
The plot thickens:
result
receives its value as8
.The inner function
multiplyByTwo
is encountered.
Intermission: A Quick Side Story
Memory Creation Phase (multiplyByTwo
Execution Context)
Memory allocates for the parameter
num
asundefined
.As a new function was invoked, a new execution context gets created i.e.
EC2
.
Code Execution Phase (multiplyByTwo
Execution Context)
The parameter
num
gets its value.The multiplication performance begins.
the
return num *2
tells the function that it is done with the work and just return the control back to the local execution context ofcalculateSum()
where the function was invoked.When it will encounter the
return num * 2
statement, it finds the value ofnum
in the local memory, performs the calculation and the control goes to the execution context ofcalculateSum()
where it will replaceundefined
ofdoubleResult
with16
.Once the whole function code is executed, the execution context for the instance of that function will be deleted.
Then,
EC2
is popped out of the stack, and the control goes back to the execution context ofEC1
.
Back to the Previous Act: Resuming calculateSum
The stories intertwine:
Next, the statement
return doubleResult
will again, return the control back to the global execution context where the function was invoked.The
finalResult
will now the get its value fromdoubleResult
.Once the whole function was executed, then
EC1
will also move out of stack, and the control goes back to Global Execution Context (GEC).
The Finale
Now, as the entire code is done with its executions, the entire global executed context is also deleted.
As this whole program is executed, the call stack gets empty. The GEC is also popped out from this call stack and we are done with our JavaScript program. So that is how the whole code inside the JS Engine is executed.
The entire flow:
The Harmony of the Call Stack
JavaScript maintains a call stack to manage the order of execution contexts.
The global execution context starts at the bottom of the stack.
Function invocations push new execution contexts onto the stack.
After execution, contexts pop off the stack.
This dance continues until the program concludes, leaving an empty call stack.
Let's take an example to understand:
function outer() {
var outerVar = "I am outer!";
function inner() {
var innerVar = "I am inner!";
console.log(outerVar); // Output: I am outer!
}
inner();
console.log(innerVar); // Error: innerVar is not defined
}
outer();
Code Flow in terms of Execution Context and Callstack:
Global Execution Context (GEC):
Memory Phase:
outer: <function code>
Call Stack: [GEC]
Execution of Function
outer()
:Local Execution Context for
outer
:Memory Phase:
outerVar: undefined
Call Stack: [GEC, outer()]
Execution of Function
outer()
(Continued):Memory Phase (Contd.):
outerVar: "I am outer!"
Execution Phase:
- Call to
inner()
pushes a new local Execution Context forinner
onto the Call Stack.
- Call to
Call Stack: [GEC, outer(), inner()]
Execution of Function
inner()
:Local Execution Context for
inner
:Memory Phase:
innerVar: undefined
Execution Phase:
Memory for
outerVar
is accessible within the scope ofinner
.console.log(outerVar)
printsI am outer!
.
Removal of
inner()
from Call Stack.
Call Stack: [GEC, outer()]
Back to
outer()
Execution:Execution continues after the call to
inner()
.Attempting to print
innerVar
in the console results in an Error sinceinnerVar
is not accessible outside its scope.Removal of
outer()
from Call Stack.
Call Stack: [GEC]
Program Conclusion:
GEC is deleted.
Call Stack is empty.
Key Takeaways
JavaScript execution starts with the creation of a global execution context.
Execution contexts consist of a Memory Component and a Code Component.
The Memory Creation and Code Execution phases manage variable and function allocation.
Function invocations create new execution contexts.
The call stack determines the sequence of execution contexts.
Now, the next time you run your JavaScript code, visualize the dance of execution contexts and the orderly steps taken by the call stack.
Happy coding!