JavaScript code execution involves a sequence of steps managed by the JavaScript engine, primarily through the use of the call stack and execution contexts. Remember, JavaScript is a synchronous (moves to the next line only when the execution of the current line is completed) and single-threaded (can execute one command at a time in a specific order) language.
Let’s first understand about Execution content.
Execution Context
Whenever a JS file executes, the JavaScript engine creates a default Execution Context known as the Global Execution Context. Everything in JavaScript happens inside an Execution Context. You can think of it as a container where the entire process occurs.
The Execution Context has two components, and JavaScript code is executed in two phases:
- Memory Component or Variable Environment
- Code Component or Thread of Execution
Inside the Execution Context
After creating the Execution Context, two phases occur:
Memory Allocation Phase (Creation Phase) In this phase, the global object (browser - window, node.js - global) is created. The
this
object is also created and bound to the global state. All variables and functions in the code get their memory allocated withundefined
and the entire function code, respectively.Code Execution Phase In this phase, thread execution happens, and all variables get their assigned values. When a function is invoked, a new Execution Context is created, and the process repeats with its own memory allocation and code execution phases.
Example
Let’s dive into an example for better understanding.
var x = 10;
var y = 5;
function doSubtraction(n1, n2) {
var sub = n1 - n2;
return sub;
}
var sub1 = doSubtraction(x, y);
var sub2 = doSubtraction(100, 50);
When executing the above code, the Creation Phase and Execution Phase will occur as follows:
Step 1: Creation Phase
x
variable is allocated memory and storesundefined
.y
variable is allocated memory and storesundefined
.doSubtraction
function is allocated memory and stores the entire function code.sub1
variable is allocated memory and storesundefined
.sub2
variable is allocated memory and storesundefined
.
Step 2: Execution Phase
- The value of 10 is assigned to the
x
variable. - The value of 5 is assigned to the
y
variable. - The
doSubtraction
function is skipped for now since there is nothing to execute. - The
doSubtraction
function is invoked, creating a new Function Execution Context.
Step 3: Function Execution Context Creation Phase
n1
andn2
variables are allocated memory and storeundefined
.sub
variable is allocated memory and storesundefined
.
Step 4: Function Execution Context Execution Phase
n1
andn2
are assigned the values 10 and 5, respectively.- The subtraction is performed, and the result (5) is stored in the
sub
variable. - The
return
statement sends the function’s result back to the Global Execution Context with the value 5. - The returned value is assigned to the
sub1
variable. - The process repeats for the next function call (
doSubtraction(100, 50)
), creating a new Function Execution Context and following the same steps from Step 3.
After the Step 4, the function context created will be removed from the call stack. Then the next function call (doSubtraction(100, 50)
) creates a new function execution context, and it will add to the call stack. After performing the calculation, the latest function execution context also removes from the call stack and control will go to the global execution context, which persists until the script fully completes.
Call stack
The call stack in JavaScript is a data structure that manages the execution orders of function calls in a last-in, first-out (LIFO) order.
The call stack is essentially a stack data structure used by the JavaScript engine to keep track of the point to which each active function should return control, when it finishes executing. When a JS program is run, the call stack is populated with this Global execution context. When a function is invoked a new function execution context is created and pushed to the stack. After the function execution it will popped out of the stack and the control reaches to the execution context below it in the stack.
Let’s explain the call stack with the above example. As you can see in the diagram, after creating global context, function execution context is pushed to the stack when the doSubtraction(x, y)
invokes and it will be popped out after the execution. Similar to that, next function call (doSubtraction(100, 50)
) creates a new function execution context, and it will pushed to the call stack. And it will popped out when the execution completes and control reaches to the execution context below it in the stack.
Summary
Hope you understood how JavaScript code is executing with the help of execution contexts and call stack.
- The global execution context is the starting point of any JavaScript execution and remains at the bottom of the call stack throughout the entire execution process.
- The global execution context has two phases: the memory allocation phase and the code execution phase.
- In the memory allocation (creation) phase, variables are allocated memory and initialized with
undefined
. Functions are also allocated memory and store their entire function code. - In the code execution phase, values are assigned to variables. When a function is invoked, a new function execution context is created and pushed onto the call stack.
- The call stack manages function calls, with each function call creating a new execution context that is pushed onto the stack.
- When a function completes, its execution context is popped off the stack, and control returns to the previous execution context.
- The call stack manages the order of function execution in a last-in, first-out (LIFO) manner.
- After all function execution contexts are popped off, control returns to the global execution context, which persists until the script fully completes.
References
GeeksforGeeks, freeCodeCamp, YT Namastae JavaScript, Traversy
Use the share button below if you liked it.
It makes me smile, when I see it.