Execution context

1. Execute the context stack

Take a look at the execution context lifecycle

Js execution context is a basic concept, ** execution context is the current code running environment, the running environment mainly includes the global environment and function environment, ** in-depth understanding of the execution context to understand the function scope chain, variable promotion, closure is very good

The Execution Context Stack follows the last in, first out principle and manages the Execution Context. When THE JS code is executed, the global environment is first entered, and the global Context is created and added to the Stack. When the corresponding function environment is entered, the function Context is created and added to the Stack. When the function code completes execution, the stack is removed

function A() {
   B()
}
function B() {

}
A()
Copy the code

We can look at Yamashita bunji in pseudocode

Ecstack. push(global_EC) // A is called ecstack. push(A_EC) // B is called, Pop () // ecstack.pop () // global context ecstack.pop ()Copy the code

2. Composition of execution context

There are three important properties in the execution context, Variable Object, scope chain, and this(this binding), which we can express as

EC = {
    VO,
    SC,
    this
}
Copy the code

The lifecycle of the execution context is the creation and execution phases, respectively.

  • The creation phase is mainly to generate the variable object, establish the scope chain, and determine the this point
  • The execution phase is mainly variable assignment and code execution

1. Variable Object

Creating a context is a three-part process

  1. Retrieves the Arguments of the current context: Generate Arguments objects and change the parameter name to the property name and the property value to the parameter

  2. Retrieves the function declaration of the current context, the function name is the property name, and the reference address of the function is the property value

  3. Retrieves the variable declaration of the current context with the name of the property, undefined being the value of the property

    VO = {Arguments: {}, ParamVariable: Function:, ParamVariable: undefined}

When the context reaches the execution stage, the variable Object becomes an Active Object and the declared variable is assigned a value

Let’s take a practical example

function A(p) {
    var a = 1
    function B() {}
    var c = function () {}
}
A(0)
Copy the code

The context creation phase of such a code generation is

A_EC = {
    VO = {
        arguments: {
            '0': 0,
            length: 1
        },
        p: 0,
        a: undefined,
        B: <function B reference>,
        c: undefined
    }
}
Copy the code

In the execution phase of A

A_EC = {
    VO = {
        Arguments: {
            '0': 0,
            length: 1
        },
        p: 0,
        a: 1,
        B: <function B reference>,
        c: <function express c reference>
    }
}
Copy the code

This is the internal mechanism of function promotion and variable promotion

var a = 1
function A() {
}
Copy the code

In fact, the order in which JS is created can be understood as

function A() {}
var a = undefined
a = 1
Copy the code

2. Scope chain

Scope chain refers to a series of variables in the current context and upper context object hierarchy consisting of a chain, it ensures that the current execution environment can access which variables and functions, when you need to find a variable or function, will find in the current context of the variable object, if not found, will be along the upper context variable object to look up, know to find the global context

Js scope includes global scope and function scope. Function scope is determined when the function is declared. Each function will contain a [[scope]] internal attribute

Function A () () {} {the function B B ()} () / / B of the [[scope]] create stage B. [[scope]] = [A_EC. VO, globalObj]Copy the code

When B is called, its execution context is created and added to the stack, which adds the generated variable object to the top of the scope chain

B.[[scope]] = [B_EC.VO, A_EV.VO, globalObj]
Copy the code

3. This point

The reference to this is determined when the function is called

You can read my previous article

Juejin. Cn/post / 693903…

Several cases of this are explained

4. The closure

When the context is removed from the stack, the corresponding variable object is collected by the garbage collection mechanism. There are two ways to collect js garbage

  1. Reference counting: This is a simple determination of whether an object points to another reference. If it does not, it will be recycled. If it does, it will not be recycled
  2. Mark clearing: Periodically scan the objects in memory from the root object, and mark the objects that are not in use as unreachable objects. You can meditate on garbage collection

Closures, however, prevent variable objects from being recycled

A closure is a function that has access to internal variables, understood in execution context terms, meaning that the current execution context is off the stack, but the current variable object can still be accessed

function A() {
    var a = 1
    function B() {
        var b = a + 1
    }
    return B
}
A()()
Copy the code

You can see from the top

The [[scope]] / / B can be expressed as b. [[scope]] = [B_EC. VO, A_EC. VO, global VO]Copy the code

So the scope chain of context B contains variable objects of context A, and B accesses variable objects of context A, preventing garbage collection of variable objects of context A

Let’s take a classic example

var arr = []
for(var i=0; i < 3; i++ ) {
    arr[i] = function() {
        console.log(i)
    }
}
arr[0]()
arr[1]()
arr[2]()
Copy the code

To examine the example, the global context can be represented as

global_EC = {
    VO: {
        arr,
        i: 3
    }
}
Copy the code

This is when arr[0] is called in context

arr[0]_EC: {
    VO: {
    },
    scope: [arr[0]_EC.VO, global_EC.VO]
}
Copy the code

Arr [0]_EC has no I variable, so it can only be found in global_EC. I = 3, so arr[1]() and ARr [2] are all global I. Before let,a classic solution was closure

var arr = [] for(var i=0; i<3; i++) { arr[i] = (function(i) { return function() { console.log(i) } } )(i) }Copy the code

So let’s analyze it again, arr[0]_EC

Arr [0] _ec. scope = [arr[0] _ec. VO, anonymous function _ec. VO, golbal_ec.vo]Copy the code

And the VO of anonymous functions is

Function _ec. VO = {Arguments: {'0': 0, length: 1}, I: 0}Copy the code

At this time, arr[0] has no I in VO, but according to the scope chain found the I of VO in the anonymous function, so it can return the expected result

Of course, you can also use let in ES6

var data = []
for( let i = 0; i<3; i++) {
    data[i] = function() {
        console.log(i)
    }
}
Copy the code

The block-level scope of the LET declaration can be thought of as an execution context, which can be seen as

{
    let i = 0
    data[0] = fucntion() {
        console.log(i)
    }
}
Copy the code

Data [0] can be regarded as the closure of the scope generated by the let, referring to I of the block-level scope, so the value of I can be obtained