The content of this article is mainly reprinted from the following two authors, if there is any infringement, please contact me to delete: https://feclub.cn/post/content/ec_ecs_hosting http://blog.csdn.net/hi_kevin/article/details/37761919

First of all, define a few concepts:

EC: Execution Context, Execution Context ECS: Execution Context Stack, Execution Context Stack VO: Variable Object, Variable Object AO: Active Object scope chain

EC (Execution context)

Each time the controller goes to ECMAScript executable code, it enters an execution context.

So what is executable code?

Type of executable code

1. Global Code

This type of code is handled at the “program” level: for example, loading external JS files or code inside local “” tags. The global code does not include any code inside the function. This is the default environment in which the code runs, and it is the first environment the engine enters once the code is loaded.

2. Function code

The code inside any function, but note that the code inside a specific function does not include the code inside the function.

3. Eval code

The code inside eval

ECS (Execution environment Stack)

We use an example from MDN to introduce the concept of function execution stack

function foo(i) {
    if (i < 0) return;
    console.log('begin:' + i);
    foo(i - 1);
    console.log('end:' + i);
}
foo(2);

/ / output:

// begin:2
// begin:1
// begin:0
// end:0
// end:1
// end:2
Copy the code

I don’t care about the result here. To make things easier, consider the concept of a function execution context stack. I believe that once you understand the following concepts, everything will fall into place.

As we all know, JS interpreters in browsers are implemented as single-threaded, which means that only one thing can happen at a time, and other actions or events are queued up in what is called the execution stack. The following figure is an abstract view of the single-threaded stack:

The same thing happens here if you call other functions inside the current function. The execution flow of the code goes into the internal function, creates a new execution context and pushes it to the top of the execution stack. The browser always executes the execution context at the top of the stack, and as soon as the current context function finishes executing, it is ejected from the top of the stack, handing control of the context to the current stack. In this way, the context in the stack is executed in turn and the stack is popped until the global context is returned. Here’s an example:

(function goo(i){
   if(i === 3) {return
  }else{
    goo(i++)
  }
}(0));
Copy the code

After the above goo has been declared, it is forced to run directly through the () operator. The function code calls itself three times, each time the local variable I is incremented by one. Each time the Goo function is called by itself, a new execution context is created. Every time a context finishes executing, it is popped off the stack and returned to the previous context until it is returned to the global context again. The whole process is abstracted as follows:

Thus, the abstract concept of execution context can be summarized as follows:

There is no limit to the number of execution contexts for a function. 5. Every time a function is called, a new execution context is created for it, even if the function itself is called

Now that you’re familiar with the reason for the output of the above example, I’ve drawn a flowchart to help you understand foo:

VO (variable object) /AO (live object)

Why do I use a/here? AO is literally activated VO, and the two are the same thing. Here’s a quote from Zhihu to help you understand. The original link

Variable object: the JS execution context has an object that holds function identifiers, parameters, Variable declarations, etc., which can be accessed in the execution context but cannot be deleted. They’re going to hang on this object, and the properties of the object correspond to their names and the values of the properties of the object correspond to their values but this object is a specification or an engine implementation that is not accessible as an active object in the JS environment.

Activation Object: With a variable object stored in each context, when can it be accessed? Every time an execution context is entered, variable objects in that context are activated, which means that function identifiers, parameters, variable declarations, and so on in that context can be accessed.

Details of EC establishment

1. Create phase [when a function is called without executing any of its internal code]

2. Create variables, functions, and parameters. 3, find the value of “this”

2. Execution phase

Initializes values of variables and references to functions, and interprets/executes code.


We can abstract each execution context into an object that has three properties

ECObj: {
    scopeChain: { /* variableObject + variableObject of all parent execution contexts */ }, 
    variableObject: { /* Function arguments/ parameters, internal variables and function declarations */ }, 
    this: {}}Copy the code

The interpreter executes the pseudo-logic of the code

1. Find the code that calls the function.

2, before executing the code, enter the create context phase:

Step 1: Initialize the scope chain Step 2: Create the variable object: a. CreateargumentsObject, checks context, initializes parameter names and values, and creates a copy of the reference. B. Scan context for function declarations (not function expressions) :1For each function discovered, a property is created on the variable object, specifically the name of the function, which has a reference to the function in memory.2If the name of the function already exists, the reference pointer is overridden. C. Variable declarations in the scan context:1For each discovered variable declaration, create a property on the variable object, which is the name of the variable, and initialize the value of the variable toundefined
        2If the variable name already exists in the variable object, no operation will be performed and the scan will continue. Step 3: Find the inside of the contextthisThe value of the.Copy the code

3. Activation/Code execution phase:

Run/interpret function code on the current context and execute the values of assigned variables line by line with the code.

VO — corresponds to the second phase above

function foo(i){ var a = ‘hello’ var b = function(){} function c(){} } foo(22)

// When we call foo(22), the entire creation phase looks like this: ECObj = {scopChain: {… }, variableObject: { arguments: { 0: 22, length: 1 }, i: 22, c: pointer to function c() a: undefined, b: undefined }, this: { … }}

As we can see, during the context creation phase, VO is initialized as follows (in order: function parameter ==>> function declaration ==>> variable declaration) :

  • Function parameter (when entered into function execution context) – a property of the variable object whose property name is the name of the parameter and whose value is the value of the argument; For arguments not passed, the value is undefined

  • FunctionDeclaration (FD) – a property of a variable object whose name and value are created by the function object; If the variable object already contains a property of the same name, replace its value

  • Var (VariableDeclaration) — a property of a variable object whose property name is the variable name and whose value is undefined; If the variable name is the same as the declared function name or parameter name of the function, the existing properties are not affected.

If a variable object already contains a property of the same name, replace the value of the object. If a variable object contains a property of the same name, replace the value of the object. Look at this code:

function foo1(a){
    console.log(a)
    function a(){} 
}
foo1(20)//'function a(){}'
Copy the code

Function a(){} = function a(){} = function a(){} = function a(); If the name of a variable is the same as the name of an already declared function or parameter, it will not affect the existing properties.

// Scenario 1: Same as the parameter name
function foo2(a){
    console.log(a)
    var a = 10
}
foo2(20) / / '20'

// Scenario 2: Same as the function name
function foo2(){
    console.log(a)
    var a = 10
    function a(){}
}
foo2() //'function a(){}'
Copy the code

Here are a few more interesting examples to savor as side dishes. Here’s a sentence for reference:

Function declarations take precedence over variables, and definition procedures are not overridden by variables, except for assignments

function foo3(a){
    var a = 10
    function a(){}
    console.log(a)
}
foo3(20) / / '10'

function foo3(a){
    var a 
    function a(){}
    console.log(a)
}
foo3(20) //'function a(){}'
Copy the code

AO — corresponds to the third stage

As we can see, the creation process is only responsible for defining the names of the properties, not assigning them specific values, and of course the parameters/arguments. Once the creation phase is complete, the execution flow enters the function and the activation/code execution phase. Here’s what the function looks like when it’s completed:

ECObj = {
    scopeChain: {... },variableObject: {
        arguments: {
            0: 22.length: 1
        },
        i: 22.c: pointer to function c()
        a: 'hello',
        b: pointer to function privateB()},this: {... }}Copy the code

Upgrade (in the following cases)

Many people will see the output of the following code at a glance, but few will be able to explain why it is produced.

(function() {
    console.log(typeof foo); // Function pointer
    console.log(typeof bar); // undefined

    var foo = 'hello',
        bar = function() {
            return 'world';
        };

    function foo() {
        return 'hello';
    }
}());
Copy the code

1. Why can we access foo before it is declared? Recall that during the VO creation phase, we knew that the function was already created in the variable object at that stage. So before the function is executed, foo is already defined. 2. Foo is declared twice. Why does foo appear as a function instead of undefined or a string? We know that during the creation phase, function declarations are created prior to variables. Moreover, in the process of variable creation, if an attribute with the same name already exists in VO, the existing attribute will not be affected. Therefore, the reference to the function foo() is first created in the live object, and when we interpret var foo, we see that the foo property name already exists, so the code does nothing and continues executing. 3. Why is the value of bar undefined? Bar is defined as a function expression, so bar is actually a variable, but the value of the variable is a function, and we know that variables are created during the creation phase but they are initialized to undefined, which is why function expressions are not promoted.

Conclusion:

1. EC is divided into two stages, creating execution context and executing code. 2, each EC can be abstracted as an object, the object has three attributes, respectively is: the Scope chain Scope, VO | AO, AO, VO can only have one) and this. 3. The AO in EC determines the attributes of the Arguments object when entering EC; When EC is executed, other variable attributes are crystallized. 4. The EC creation process is in the following order: parameter declaration > function declaration > variable declaration.

reference

Javascript execution environment, variable object, scope chain

What is the Execution Context & Stack in JavaScript?

Function MDN