Demystifying the JavaScript Call Stack: How Your Code Really Runs
How the JavaScript call stack works is something that every frontend developer has asked at least once in his career, and in my opinion, it's not a question that has been answered in most places, and the answers are not always clear or easy to understand. That's why I've decided to cover the topic in this post.
Let's start from the beginning. The JavaScript engine runs the code line by line synchronously, and every time a function is executed, it creates an execution context (a space in memory to store all the scoped properties that exist only inside that function) and adds the function to the call stack.
JavaScript only executes the code of the function that is on the top of the stack, and when a function finalises and returns its value, the engine removes the function from the call stack and starts working on the next one.
When the call stack is empty, the JavaScript engine continues running the code in the next global context, or what is the same, continues executing the lines that are in the root of the JavaScript file and don't belong to any function.
Let's see some examples, line by line:
const num1 = 2; const num2 = 5; function sum(a, b){ return a + b; } const result= sum(num1, num2); console.log(result) // 7
This is a really simple code that defines 2 constants (num1 and num2) and then defines a function sum that sums 2 numbers and returns the result of the sum. Finally, the constant result is created, and the result of executing sum with the arguments num1 and num2 is assigned to it. Then the value of the result is printed on the console.
If you think that the previous explanation is too simple or too complex and does not explain anything, please bear with me, we are getting to the interesting stuff.
Let's see what the JavaScript engine is doing, line by line. In the first line, the engine creates a label num1 and stores in memory the value 2.
const num1 = 2;
The second line does the same for label num2. It creates a label num2 and stores in memory the value 5.
const num2 = 5;
What this means is that whenever someone inside the global scope requires the value of num2 the engine is going to change the label and put the value 5 instead.
Let's continue with the next line. The next line is the function sum. What do you think the engine is going to do? Do you think it is going to execute the function or add it to the call stack? Nope! What the engine is going to do is store a new label named sum and store the code inside the brackets in memory. Or what is the same, It's going to store the function definition and assign it to the sum label.
function sum(a, b){ return a + b; }
if we could visually see the memory of the code that we've run so far, we would see something like this:
Label name | Value in memory |
---|---|
num1 | 2 |
num2 | 5 |
sum | function definition |
The next line is a really interesting one. When the JavaScript engine reaches the next line, it creates the label result, but at this point, it doesn't yet know what value needs to be assigned to the label because the value is the result of executing a function, so first, it needs to execute the function, do whatever the function needs to do, and get the result from the return value.
const result= sum(num1, num2);
At this point, the engine adds the function to the call stack and also creates a new execution context, which is a new space in memory were JavaScript can store the value of the args and every other property that is inside of the function without colliding with the global context.
Call stack |
---|
sum |
Global |
First of all, the engine creates in memory the labels a and b which are the names given to the parameters, and it assigns the value of the arguments 2 and 5 respectively.
If we could see the memory at this specific moment, we would see something like this:
Label name | Value in memory |
---|---|
a | 2 |
b | 5 |
return | 2 + 5 = 7 |
In this case, the function is really simple and only returns the value of the sum between a and b, so the engine substitutes the parameters with the values of the arguments and returns the value to the global execution context. Finally, the function is removed from the call stack, and only the global context remains.
Call stack |
---|
Global |
At this point, the result of the function is assigned to the label result and we can print the value on console with the console log.
Let's take a look at how the global memory looks now:
Label name | Value in memory |
---|---|
num1 | 2 |
num2 | 5 |
sum | function definition |
result | 7 |
Did you noticed? the label result has a value of 7? and also sum still has the function definition inside.
Let's take a look at the next code:
const num1 = 2; const num2 = 5; function sum(a, b){ return a + b; } const result= sum(num1, num2); console.log(result) // 7 function sumThreeNumbers = (x,y,z) => { return sum(x, y) + z } const result2 = sumThreeNumbers(4,6,2) console.log(result2) // 12
The main difference is that now we have a new sumThreeNumbers function and we are creating a new result2 constant and assigning the value of running the function sumThreeNumbers with the arguments, 4, 6 and 2.
Let's take a look at how the call stack works when we run nested functions.
If we jump to the line when we define the constant result2 the global memory would look something like this:
Label name | Value in memory |
---|---|
num1 | 2 |
num2 | 5 |
sum | function definition |
result | 7 |
sumThreeNumbers | function definition |
Just as on the previous example, the JavaScript engine doesn't know what value to assign to the label result2, to get the value, first needs to execute the function sumThreeNumbers with the arguments. The function is added to the call stack, and a new execution context is created. The execution context would look like this:
Label name | Value in memory |
---|---|
x | 4 |
y | 6 |
z | 2 |
So the first thing that JavaScript does is create the parameter labels and assign the value provided by the arguments.
Now let's take a look at our call stack
Call stack |
---|
sumThreeNumbers |
Global |
As you can see, the call stack only has the sumThreeNumbers item (apart from the global context that is always present).
To be able to get the result value, the function sum needs to be executed first, so the engine will add the function to the call stack and create a new execution context for the sum function.
Call stack |
---|
sum |
sumThreeNumbers |
Global |
As the sum function is on top of the call stack, Javascript needs to run sum first to be able to continue running sumThreeNumbers.
This is how it's going to look the execution context for the function sum:
Label name | Value in memory |
---|---|
a | 4 |
b | 6 |
return | 4 + 6 = 10 |
As you know, it will return _10 _and it will be removed from the call stack
Call stack |
---|
sumThreeNumbers |
Global |
The JavaScript is going to continue executing the sumThreeNumbers and the execution context will look like this:
Label name | Value in memory |
---|---|
x | 4 |
y | 6 |
z | 2 |
return | 10 + 2 = 12 |
It will return the value 12 and be removed from the call stack.
Call stack |
---|
Global |
Then the value 12 will be assigned to the property result2 and the value 12 will be displayed in the console.
I hope this post has help you to understand how the JavaScript call stack works, if so please leave a comment and a like. I see you in the next one!
The above is the detailed content of Demystifying the JavaScript Call Stack: How Your Code Really Runs. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics











Different JavaScript engines have different effects when parsing and executing JavaScript code, because the implementation principles and optimization strategies of each engine differ. 1. Lexical analysis: convert source code into lexical unit. 2. Grammar analysis: Generate an abstract syntax tree. 3. Optimization and compilation: Generate machine code through the JIT compiler. 4. Execute: Run the machine code. V8 engine optimizes through instant compilation and hidden class, SpiderMonkey uses a type inference system, resulting in different performance performance on the same code.

Python is more suitable for beginners, with a smooth learning curve and concise syntax; JavaScript is suitable for front-end development, with a steep learning curve and flexible syntax. 1. Python syntax is intuitive and suitable for data science and back-end development. 2. JavaScript is flexible and widely used in front-end and server-side programming.

JavaScript is the core language of modern web development and is widely used for its diversity and flexibility. 1) Front-end development: build dynamic web pages and single-page applications through DOM operations and modern frameworks (such as React, Vue.js, Angular). 2) Server-side development: Node.js uses a non-blocking I/O model to handle high concurrency and real-time applications. 3) Mobile and desktop application development: cross-platform development is realized through ReactNative and Electron to improve development efficiency.

This article demonstrates frontend integration with a backend secured by Permit, building a functional EdTech SaaS application using Next.js. The frontend fetches user permissions to control UI visibility and ensures API requests adhere to role-base

I built a functional multi-tenant SaaS application (an EdTech app) with your everyday tech tool and you can do the same. First, what’s a multi-tenant SaaS application? Multi-tenant SaaS applications let you serve multiple customers from a sing

The shift from C/C to JavaScript requires adapting to dynamic typing, garbage collection and asynchronous programming. 1) C/C is a statically typed language that requires manual memory management, while JavaScript is dynamically typed and garbage collection is automatically processed. 2) C/C needs to be compiled into machine code, while JavaScript is an interpreted language. 3) JavaScript introduces concepts such as closures, prototype chains and Promise, which enhances flexibility and asynchronous programming capabilities.

The main uses of JavaScript in web development include client interaction, form verification and asynchronous communication. 1) Dynamic content update and user interaction through DOM operations; 2) Client verification is carried out before the user submits data to improve the user experience; 3) Refreshless communication with the server is achieved through AJAX technology.

JavaScript's application in the real world includes front-end and back-end development. 1) Display front-end applications by building a TODO list application, involving DOM operations and event processing. 2) Build RESTfulAPI through Node.js and Express to demonstrate back-end applications.
