How JavaScript Executes Your Code
When you run a JavaScript program, a lot of things happen behind the scenes within the JavaScript engine.
Let’s try to understand that with the help of this JavaScript program:
var n = 2; function square(num) { var ans = num * num; return ans; } var square2 = square(n); var square4 = square(4);
Everything in JavaScript executes within an "execution context." You can think of this execution context as a large box or container in which all JavaScript code is executed.
The execution context consists of two main components. The first component is known as the Memory Component. This is where all variables and functions are stored as key-value pairs.
A more technical term for this memory component is the Variable Environment.
The second component of the execution context is the Code Component. This is where the code is executed one line at a time.
Another technical term for this code component is the Thread of Execution. The Thread of Execution functions like a thread, executing the entire code one line at a time.
Now, the execution context is created in two phases. The first phase is known as the Creation Phase, also called the Memory Creation Phase. Let’s see how this phase works.
In the creation phase, JavaScript allocates memory to all variables and functions.
As soon as JavaScript encounters line 1, it allocates memory for n
. How does it do this? It reserves a memory space for n
and initially stores a special value called undefined
.
Then, JavaScript moves to line 2, where it sees a function named square
. It allocates memory to the square
function, and in the case of functions, it stores the entire function code in memory.
Similarly, it allocates memory to square2
and square4
. Since these are variables, they also start with the value undefined
.
Now, let’s see what happens in phase 2, the Code Execution Phase. This is where the actual code execution occurs after memory allocation.
In this phase, JavaScript again goes through the entire program line by line and executes the code. This is the point where function calls and all calculations are performed.
As soon as it encounters var n = 2
on line 1, it assigns the value 2
to n
. Up until this point, n
was undefined
. Now, in this second phase, n
gets the value 2
.
After completing line 1, it moves to line 2, but there’s nothing to execute here. The same goes for lines 2–5. So, it moves on to line 6.
On line 6, we invoke a function. Whenever you see a function name followed by parentheses ()
, that means the function is being executed.
Functions in JavaScript are like mini-programs. When a function is invoked, a new execution context is created.
Remember, the entire program runs inside the global execution context. When you invoke a function, a brand-new execution context is created.
This new execution context also has two components: the memory component and the code component. It goes through the same two phases as before.
In the memory creation phase, memory is allocated to the function’s variables and parameters, like num
and ans
. In this phase, both num
and ans
are initialized with undefined
.
Now comes the code execution phase. When the function is invoked, the value of n
is passed to num
, and the program moves to the next line.
On line 3, the calculation num * num
takes place, and the result is stored in ans
. This calculation happens inside the code component, and the value is then stored in ans
.
When the program reaches line 4, it sees the return
keyword, which means the control is handed back to the point where the function was called. The value of ans
is returned, and it replaces the undefined
value in square2
.
Once the function completes execution, its execution context is deleted.
Now, let’s move to line 7. Here, we are executing the function again, but this time passing 4
as the argument directly.
When the function is invoked again, a new execution context is created. As before, it has a memory component and a code component, and it goes through the two phases.
In the first phase, memory is allocated to num
and ans
, and both are initialized to undefined
.
In the second phase, num
is assigned the value 4
, replacing undefined
. The program moves to line 3, where num * num
is calculated as 16
. This value is stored in ans
.
The control then moves to line 4, where the return
statement returns the value of ans
to line 7, replacing the undefined
value in square4
. As soon as the function finishes execution, its execution context is deleted.
The program is now done with line 7, and there’s no more code to execute. The entire global execution context is also deleted, signaling that the program is finished.
Call Stack
JavaScript manages the creation, deletion, and control of execution contexts using a stack known as the Call Stack.
So, what is a call stack? It's a stack where the global execution context is always at the bottom. Whenever a JavaScript program runs, the call stack is populated with the global execution context.
When a function is invoked, a new execution context is created and pushed onto the stack. For example, when we invoked the function in line 6, its execution context (let’s call it E1
) is pushed onto the stack.
Once the function finishes execution, the E1
context is popped off the stack, and control returns to the global execution context.
On line 7, when another function is invoked, a new execution context (let’s call it E2
) is created and pushed onto the stack. After the function completes execution, E2
is also popped off, and control returns to the global execution context.
The call stack is responsible for managing these execution contexts. Every time an execution context is created, it is pushed onto the stack, and when it’s deleted, it is popped off the stack.
After the entire program is executed, the call stack becomes empty, and the global execution context is removed as well.
Get my free, weekly JavaScript tutorials
Want to improve your JavaScript fluency?
Every week, I send a new full-length JavaScript article to thousands of developers. Learn about asynchronous programming, closures, and best practices — as well as general tips for software engineers.
Join today, and level up your JavaScript every Sunday!
Thank you, Taha, for your amazing newsletter. I’m really benefiting from the valuable insights and tips you share.