This is the 23rd day of my participation in the August More Text Challenge

The concepts of execution context and call stack are always with us, and every line of code we write is closely related to them. Understanding these two execution logic hidden behind the code helps us to write better code

Execution context

The execution context is the execution environment/scope of the current code. Intuitively, execution contexts contain scope chains, but they are also like the upstream and downstream of a river: the scope chain is part of the execution context.

Two phases of code execution

To understand these two concepts, it is necessary to start from the execution process of JS code, which is not involved in normal development, but is very important for us to understand the JS language and operation mechanism. JS execution is divided into two phases:

  • Code precompilation phase
  • Code execution phase

The precompilation stage is the pre-compilation stage, when the compiler compiles JS code into executable code. Note that this is not the same as traditional compilation, which is very complex and involves word segmentation, parsing, code generation, and so on. Precompilation here is a unique concept in JS, even though JS is an interpreted language, compiling one line, executing one line. But the JS engine does do some “preparatory work” before the code executes.

In the execution phase, the main task is to execute the code, and the execution context is created in this phase.

After passing the grammar analysis and confirming the correct syntax, the JS code allocates the memory space of the variable in the pre-compilation stage, and the variable promotion process we are familiar with is completed in this stage. The following code:

After the precompilation process, we should note three things:

  • Variable declaration during precompilation;
  • The variable declaration is promoted in the precompilation phase, but the value is undefined.
  • All non-expression function declarations are promoted during precompilation.

Let’s take a look at some code

console.log(address);
getAge();
var address = 'shanghai'
function getAge() {
  console.log(18)}Copy the code

You can see the output from the console: undefined, 18

The output is not the same as saying that JS code is executed sequentially, because the scope is determined during the precompilation phase, but the scope chain is fully generated during the creation phase of the execution context. This is because the function starts to create its execution context when it is called. The execution context includes a variable object, a scope chain, and a reference to this

The call stack

Call stack is a data structure used to manage the function call relationship: JS engine uses the stack structure to manage the execution context. After the execution context is created, JS engine will push the execution context into the stack, which is usually called the execution context stack, also known as the call stack.

function foo1() {
  foo2()
}
function foo2() {
  foo3()
}
function foo3() {
  foo4()
}
function foo4() {
  console.log('foo4')
}
foo1()
Copy the code

Call relationship: foo1 → foo2 → foo3 → foo4 This process starts with foo1 calling foo2, then foo2 calling foo2, and so on, foo3, foo4, until foo4 is finished — foo4 first, then foo3, then foo2, then foo1. This process is “first in, last out” (” last in, first out “) and is therefore called the call stack.

Normally, when a function completes execution and exits the stack, local variables in the function will be collected at the next garbage collection node, and the corresponding execution context of the function will be destroyed, which is why variables defined in the function cannot be accessed from the outside world. That is, the variable is accessible to the function only when the function is executing, it is created during precompilation, activated during execution, and the context is destroyed after the function is executed.