Sequential execution?

Let’s start with these two pieces of code

var foo = function () {

    console.log('foo1');

}

foo();  // foo1

var foo = function () {

    console.log('foo2');

}

foo(); // foo2
Copy the code
function foo() {

    console.log('foo1');

}

foo();  // foo2

function foo() {

    console.log('foo2');

}


foo(); // foo2
Copy the code

From the above two pieces of code, we can see the obvious difference, and the printed results are different. Anyone who has scanned an interview question knows that this is because JavaScript engines don’t analyze execution line by line. When a line of code is executed, a “prep” is done, such as a variable promotion in the first example and a function promotion in the second example.

But what this article really wants us to think about is: how is this “paragraph by paragraph” divided?

What kind of code does a JavaScript engine “prepare” for?

Executable code

Which brings us to what types of JavaScript executable code are there?

There are three simple ones: global code, function code, and eval code

For example, when a function is executed, there is preparation, which is, to use a more technical term, an “execution context.”

There are also three types of execution context:

Global execution context: It does two things: 1. Creates a global object, which in the browser is the Window object. 2. Point the this pointer to the global object. Only one global execution context can exist in a program

Function execution context: Each time a function is called, a new execution context is created for that function. Each function has its own execution context, but is created only when the function is called. Any number of function execution contexts can exist in a program. Each time a new execution context is created, it performs a series of steps in a specific order, as discussed later in this article.

Eval execution context: Code running in the Eval function also gets its own execution context, but since Eval is not often used by JavaScript developers, it is not covered here.

The lifecycle of the execution context

The execution context life cycle consists of three phases: create phase -> execute phase -> reclaim phase

  1. Create a stage

When a function is called, but before any of its internal code is executed, it does three things:

  • Create a variable object: first initialize the function arguments and promote the function declaration and variable declaration.
  • Create Scope Chain: During the creation phase of the execution context, the Scope Chain is created after the variable object. The scope chain itself contains objects. Scope chains are used to resolve variables. When asked to parse a variable, JavaScript always starts at the innermost layer of code nesting, and if the inner layer doesn’t find the variable, it jumps up to the parent scope until the variable is found.
  • Make sure this refers to: including multiple cases.

Before a JAVASCRIPT script is executed, the code must be parsed. When parsing, a global execution context is created, and variables and function declarations are extracted from the code to be executed. The variable is temporarily assigned to undefined, and the function is declared ready to use. This step is done, and then the formal execution of the procedure begins.

In addition, before a function is executed, a function execution context is created, just like the global context, except that the function execution context includes this arguments and function arguments. In the execution phase, variable assignment and code execution are performed. 3. In the reclamation phase, the execution context is removed from the stack and waits for the VM to reclaim the execution context

Execution context stack

We’ve written so many functions, how do we manage so many execution contexts?

So the JavaScript engine creates an Execution Context stack (ECS) to manage the Execution context

To simulate the behavior of the execution context, let’s define the execution context stack as an array:

ECStack = [];
Copy the code

When JavaScript starts to interpret execution code, the first thing it encounters is global code, so initialization pushes a global execution context onto the execution context stack, which we call a globalContext, and only when the entire application ends, The ECStack is cleared, so there is always a globalContext at the bottom of the ECStack before the program ends:

ECStack = [
    globalContext
];
Copy the code

When a function is executed, an execution context is created and pushed onto the execution context stack. When the function is finished, the execution context of the function is ejected from the stack. With that in mind, let’s see what we can do with the code above:

// pseudocode // fun1() ecstack.push (<fun1> functionContext); Ecstack.push (<fun2> functionContext); // fun2 also calls fun3! ECStack.push(<fun3> functionContext); Ecstack.pop (); Ecstack.pop (); Ecstack.pop (); // Javascript then executes the following code, but there is always a globalContext at the bottom of the ECStackCopy the code

Example:

var scope = "global scope";
function checkscope(){
   var scope = "local scope";
   function f(){
       return scope;
   }
   return f();
}
checkscope();
Copy the code
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();
Copy the code

The execution context stack changes differently for these two pieces of code:

ECStack.push(<checkscope> functionContext);
ECStack.push(<f> functionContext);
ECStack.pop();
ECStack.pop();
Copy the code

The second code:

ECStack.push(<checkscope> functionContext);
ECStack.pop();
ECStack.push(<f> functionContext);
ECStack.pop();
Copy the code