This is the 29th day of my participation in the August Wenwen Challenge.More challenges in August
When a piece of code is executed, the JavaScript engine first compiles it and creates the execution context. But it’s not clear what exactly qualifies as conforming code.
So let’s figure out when code counts as a “piece” of code, where it is compiled and the execution context is created before execution. Generally speaking, there are three cases
- When JavaScript executes global code, the global code is compiled and the global execution context is created, and there is only one global execution context for the lifetime of the page.
- When a function is called, the code inside the function is compiled and the function execution context is created. Generally, the function execution context is destroyed when the function is finished.
- When the eval function is used, the eval code is also compiled and the execution context is created.
Now that you have a better understanding of the execution context, let’s build on that in this section and talk about the call stack. There are at least three benefits to learning the call stack:
- It helps you understand how the JavaScript engine works.
- The ability to debug JavaScript code;
- Help you nail the interview, because during the interview process, call stack is also a very high rate of exit topic
For example, when you are writing JavaScript code, you may sometimes encounter a stack overflow error, as shown in the following figure:
So why did this happen? This involves the contents of the call stack. You should be aware that there are many functions in JavaScript, and it is often the case that one function calls another function. The call stack is a data structure used to manage the relationship between function calls. So to understand the call stack, you need to understand function calls and the stack structure
What is a function call
A function call is a function run using the name of the function followed by a pair of parentheses. Let’s look at a simple example code
var a = 2
function add(){
var b = 10
return a+b
}
add()
Copy the code
This code is simple, creating an Add function and then calling that function at the bottom of the code.
So let’s use this simple code to explain the process of a function call.
Before the add() function is executed, the JavaScript engine creates a global execution context for the code above, which contains declared functions and variables, as you can see below:
As you can see from the figure, both global variables and functions in the code are stored in the variable environment of the global context.
Once the execution context is ready, the global code is executed, and when it reaches add, JavaScript determines that this is a function call and does the following:
- First, extract the add function code from the global execution context.
- Second, compile the code for the Add function and create the execution context and executable code for the function.
- Finally, execute the code and print the results.
You can refer to the following figure for the complete process:
Thus, when the add function is executed, we have two execution contexts — the global execution context and the add execution context.
That is, there can be multiple execution contexts when executing JavaScript. How does the JavaScript engine manage these execution contexts?
The answer is managed through a data structure called a stack. So what is a stack? How does it manage these execution contexts?
What is a stack
On the stack, you can be combined with the example of such an apt to understand, a one-lane road, one end is blocked, and the other end at the entrance is not any message, after plug can only go in the car after first, then the block of the road, it can be seen as a stack container, the car into the operation of the road, called the stack, The operation of backing out a car is called making a stack.
In the scene of large traffic flow, repeated loading, full, out, empty and re-loading will occur, and the cycle will continue.
So a stack is like a one-way street with one end blocked, and a car is like an element in the stack, and the elements in the stack are lifO. You can see the picture below:
What is a JavaScript call stack
JavaScript engines use this stack structure to manage execution context. After the execution context is created, the JavaScript engine pushes the execution context onto the stack, which is often referred to as the execution context stack, or call stack.
To give you a better understanding of the call stack, let’s look at a slightly more complex sample code:
var a = 2
function add(b,c){
return b+c
}
function addAll(b,c){
var d = 10
result = add(b,c)
return a+result+d
}
addAll(3.6)
Copy the code
In the above code, you can see that it calls the add function in the addAll function. How does the call stack change during the execution of the code?
Let’s analyze step by step the state of the call stack during the execution of the code.
The first step is to create the global context and push it to the bottom of the stack. As shown in the figure below
As you can see from the figure, the variable A, functions add, and addAll are stored in the variable environment object of the global context.
Once the global execution context is pushed onto the call stack, the JavaScript engine starts executing the global code. The assignment of a=2 is performed first, which sets the value of a in the global context variable environment to 2. The state of the global context after setting is shown below:
Next,The second step is to call the addAll function. When the function is called, the JavaScript engine compiles the function, creates an execution context for it, and finally pushes the execution context of the function onto the stack, as shown below:
D =10; d=10; d=10; d=10
Then proceed to step 3. When the add function call statement is executed, the execution context is also created for it and pushed onto the call stack, as shown below:
When add returns, the execution context of the function is popped off the top of the stack and the value of result is set to the return value of add, which is 9. As shown below:
Immediately after addAll performs the last addition and returns, addAll’s execution context pops off the top of the stack, leaving only the global context in the call stack. The final picture is as follows:
At this point, the JavaScript process is complete.
Well, now you know that the call stack is a mechanism that the JavaScript engine uses to track function execution. When multiple functions are called at once, the call stack allows you to track which function is being executed and the call relationships between functions.
How to take advantage of the call stack in development
Given the importance and usefulness of the call stack, let’s take a look at how to view and leverage the call stack in the real world.
1. How to use the browser to view the call stack information
When you execute a complex piece of code, it may be difficult to analyze the call relationship from the code file. In this case, you can put breakpoints in the function you want to view, and then see the call stack of that function when it is executed.
This may sound a bit abstract, but to demonstrate this, you can open developer Tools, click the “Source” TAB, select the page of your JavaScript code, add a breakpoint at line 3, and refresh the page. You can see that when the add function is executed, the execution process stops. At this point, you can check the current call stack through the “Call Stack” on the right, as shown below:
As can be seen from the figure, the function call relationship is displayed under the right “Call stack” : At the bottom of the stack is anonymous, which is the global function entry; In the middle is the addAll function; At the top is the add function. This clearly reflects how functions are called, so the call stack can be useful when analyzing complex structured code or checking for bugs.
In addition to using breakpoints to view the call stack, you can use console.trace() to print the current function call relationship. For example, add console.trace() to the add function in the sample code, and you can see the console output as shown below:
2. Stack Overflow
Now you know that the call stack is a data structure that manages the execution context, following the last in, first out rule. Note, however, that the call stack has a size, and when more than a certain number of execution contexts are pushed, the JavaScript engine will report an error called stack overflow.
Especially when you’re writing recursive code, it’s easy to run out of stacks. Take this code for example:
function division(a,b){
return division(a,b)
}
console.log(division(1.2))
Copy the code
When executed, a stack overflow error is thrown, as shown below:
The Maximum call stack size exceeded the Maximum call stack size exceeded.
So why is this a problem? This is because when the JavaScript engine starts executing this code, it first calls the function division and creates the execution context, pushing it onto the stack; However, this function is recursive and does not have any termination conditions, so it keeps creating new function execution contexts and repeatedly pushing them onto the stack, but the stack is limited in capacity, and the error of stack overflow occurs after the maximum number is exceeded.
Once you understand the cause of stack overflows, you can use methods to avoid or solve stack overflows, such as changing the form of recursive calls to other forms, or using the addition timer method to break up the current task into many smaller tasks.
conclusion
Here’s a summary:
- Each time a function is called, the JavaScript engine creates an execution context for it, pushes the execution context onto the call stack, and then the JavaScript engine starts executing the function code.
- If another function B is called in function A, the JavaScript engine creates an execution context for function B and pushes the execution context of function B to the top of the stack.
- After the current function completes execution, the JavaScript engine pops the execution context of that function out of the stack.
- A “stack overflow” problem occurs when the allocated call stack space is used up.
- Stack is a very important data structure, not only used in JavaScript language, other programming languages, such as C/C++, Java, Python, etc., also use stack to manage the call relationship between functions in the execution process. So the stack is a very basic and important knowledge that you have to master.