The Context of variables or functions determines what data they can access and how they behave, so the Execution Context is important in JavaScript.

Classification of execution context

1.1 Global Execution Context

The global context is the outermost context. Depending on the hosting environment of ECMAScript, the object representing the global context is different. In the browser context, the global context is the Window object. In the Node context, the global context is global. Since it is associated with the host environment, there is one and only one global context in any environment. This article focuses on the browser environment, which we can find through this.

All global variables and functions defined by var become properties and methods of the Window object. But top-level declarations that use let or const declarations are not defined to have the same effect in a global context through the scope chain. Windows already has a number of built-in methods and properties that can be used anywhere in the world. The context is destroyed after all its code has been executed, freeing up memory, and the global context is destroyed when the web page is closed or the browser exits.

1.2 Function execution Context

Each function call has its own context. When code execution enters the function, the function execution context is pushed onto the ECStack, and when the function is finished, the function execution context is pushed off the stack. Note that there can be more than one function execution context. A function execution context is created for each function execution, and a new function execution context is created for the same function called multiple times

1.3 Eval Function execution context

Code running in the eval function can also get its own context, but eval isn’t used much in actual development, so I won’t discuss it here.

1.4 Code Operation

Browsers want code to execute by providing an environment for code to execute. We call this environment the execution environment stack ECStack. The browser also puts some of the built-in properties and methods into a separate heap, called The Global Object or GO, that you can call later. The browser tells the window to point to GO, so the window represents the global object on the browser side.

After the browser environment is provided, we start to let the code execute. Each execution context has its own execution context, and each execution context has an associated variable object (VO) on which all variables and functions defined in this context reside. Variable objects vary from execution context to execution context.

In the global context, a variable object is a global object; in the browser context, a window. In the context of function execution this variable object is represented as an AO activation object. This is because the VO variable object is implemented on the engine and cannot be accessed directly in the JS environment. It is only when the function is called that the variable object is activated as an active object AO. We can access the properties and methods in it, the active object AO which is actually the variable object VO, but it’s in a different state to see if it’s activated, so you can view it as a branch of VO.

The JavaScript code runs on the browser side, and the global context is entered into the execution environment ECStack. This process is called “pushing”, because the execution environment stack is also a stack structure with the characteristics of stack structure first and then out. Some global variables are created in the global execution context. These global variables and their values are placed in the VO(G) variable object. The code executes from top to bottom, as shown below:

  • Create a value. At creation time, if it is a basic data type value, it can be stored directly in the stack memory. If it is a reference data type value, a new heap is created, and the contents are stored. Finally, the hexadecimal address of the heap memory is put into the stack memory for variable association.
  • Then create the corresponding variables
  • Finally, associate values with variables (all pointer assignments are pointer associative)
var a = 12;
var b = a;
b = 13;
console.log(a);
var n = {
    name: 'davina'
}
var m = n;
m.name = 'lisa';
console.log(n.name);
Copy the code
var a = {n:12};
var b = a;
a.x = a = {n:13};
console.log(a.x);
console.log(a);
Copy the code

When a function call occurs, the newly formed function execution context goes to the top of the stack. ECStack executes the functions at the top of the stack and pushes the global context to the bottom of the stack. When the function completes, an out of stack operation is performed, and control of the execution environment stack is moved down as follows:

var arr = [12.20];
function fn1(){
   console.log('*****fn1 start********');
   fn2();
   console.log('*****fn1 end***********')}function fn2(){
    console.log('*****fn2*************');
}
fn1();
Copy the code

The above code execution is divided into the following steps:

  • The browser provides an environment for code execution, ECStack;
  • The code runs from top to bottom, generating the global context and pushing it into the ECStack;
  • When a fn1 function is called, the JS engine creates a function Execution Context (fn1 func Execution Context) for it and places it on top of the ECStack.
  • The js engine creates a new fn2 func Execution Context for the fn2 function and pushes it to the top of the ECStack.
  • When the fn2 function completes Execution, its Execution Context (fn2 func Execution Context) is removed from the stack, and the control of the Context is moved down to the fN1 Execution Context
  • After the fn1 function is executed, its execution context is also ejected from the ECStack, and the control of the context is moved down to the global execution context.
  • When all code has been executed, the JS engine removes the global execution context from the ECStack.

Context code, when executed, creates scope chains of variable objects. The scope chain determines the order in which code at each level of context accesses variables and functions. When a variable is searched, it is first searched from the variable object in the current context. If there is none, it is searched up to the variable object in the parent scope. Finally, it finds the variable object in the global context. Thus a linked list of variable objects in multiple execution contexts is called a scoped chain.

What does a scope chain have to do with context? In the context of function execution, scope chain is a look-up mechanism. If you want to find a variable in a function, you need to get the value of the variable through the execution context corresponding to the scope. The scope is static, while the execution context is dynamic. And even in the same scope, for the same function of different produce different function called private execution context, which in turn will produce different values, the value of the variable scope is determined in the process of execution, and scope ([[scope]]) is a function is created when identified, it is the current function of the context.

The lifecycle of the execution context

Before we look at the execution context lifecycle, what is variable promotion and what is function declaration promotion?

2.1 Variable promotion

In the current context, all content with the “var/function” keyword in the current context is declared and defined by the browser before the js code is executed. Parsing to their corresponding context location (this is part of lexical parsing, which takes place before code execution) is a pre-processing mechanism called variable promotion. The meaning of variable promotion is to use the variable without error before creating it. It can also be called “pre-parsing”. From this we can see that variable declaration has the following steps:

  • Declare :var a/function fn
  • Defined: Assigns values to corresponding variables

In the variable promotion phase, only declarations with var are undefined, while declarations with function are complete.

Promotion occurs only in the current context, only in the global context at the start of the page load, when the function heap space stores only strings. Browsers are lazy and do not repeat what they have done, meaning that when code execution encounters the creation of a function, it skips it (the function assignment has already been done in the promotion phase).

In the context of function execution, var variables are declared private during the promotion phase and have nothing to do with the outside world.

console.log(g, h); // undefined
var g = 12,
    h = 12;
function fn() {
    console.log(g, h); //undefined
    var g = h = 13;
    console.log(g, h); / / 13 13
}
fn();
console.log(g, h); / / 12 of 12
Copy the code

Function fn(){} var fn=function fn(){} var fn=function fn(){} Var fn2 = function(){} Var fn2 = function(){} var fn2 = function(){} In this case, the value of fn2 is undefined, so the execution fails. In the case of fn1, the function declaration, it is an overall upgrade, so it can be performed.

When we encounter a function name that is the same as a variable name and both are promoted, the function declaration takes precedence and the variable declaration is overwritten, but it can be reassigned.

function fn() {
    // fn1(); //fn1
    fn2(); //Uncaught TypeError: fn2 is not a function
    function fn1() {
        console.log('fn1');
    }
    var fn2 = function fn2() {
        console.log('fn2');
    }
}
fn();
Copy the code

Both global and functional contexts have two distinct stages of creation and execution, which are described below.

2.2 Life Cycle

2.2.1 Creation Phase

In the context creation stage, the main steps are as follows: generate variable objects, establish scope chains, and determine the this point.

In the global execution context of the browser environment, this refers to the window, and the variable object is the global object.

In the context of function execution, the following steps are taken:

  • Create a variable object:
    • Create arguments objects and add parameter names and values to the variable objects
    • Scanning function, variable promotion and function declaration promotion.
  • Create scope chain: The scope chain is created after the variable object and contains the variable object itself. Scope chains are used primarily for variable resolution. When parsing a variable, first look from the scope of the variable. If not, look up the chain of scopes to see if the variable exists in the parent scope. If not, look up until the global scope is found.
  • Confirm the direction of this: This direction can contain a number of situations. In a global execution context this always points to a global object, as in a browser context this points to a window. In the context of function execution, the reference to this depends on how the function is called. If it is called by an object then this refers to that object, otherwise this refers to window or, in strict mode, this refers to undefined.

2.2.2 Execution Phase

In the execution phase, the JS engine starts to complete operations such as the assignment function of the variable. If there is a function call, a new function private execution context will be generated and pushed to the execution environment stack (ECStack) for parsing.

var num1 = 12;
function fn() {
    console.log(num1); //undefined
    var num1 = 13;
    console.log(num1); / / 13
}
fn();
console.log(num1);/ / 12
Copy the code

The above code will be parsed through the following steps:

  • The browser generates an environment for code execution, ECStack;
  • Execute global code, first generate a global context, into the ECStack execution;
  • Global context initialization. In the browser environment, this refers to window, and the variable environment is window, and the variable environment is promoted and the function declaration is promoted, and the function heap space is all strings, and there are some built-in properties. Var is just a declaration, function is both declared and defined. The context in which a function is created is its scope [[scope]].
  • The code executes from the top down, assigning values to variables. When it comes to fn, the browser is lazy and skips the heap of functions that were created and defined when the function declaration was promoted;
  • Function execution. The purpose of function execution is to execute the string of code previously stored in heap space. Code execution must have its own execution environment, forming the function fn’s own private context.
  • The newly formed function context is pushed to the top of the stack, pushing the global execution context down. When it’s done, it’s out of the stack
  • In the context of function execution, it also has a create phase and an execution phase, which goes through the following steps:
    • Initialize the scope chain [[scope chain]], one side of the scope chain is the context of the function itself, one side is the scope of the function [[scope]];
    • Initialize this: The value of this is determined at execution time;
    • Initialize the arguments collection to create an active object;
    • Parameter assignment: Parameter variables are also placed in the function context of the private variable object AO;
    • Variable ascension
    • Execution phase
  • When the execution context of a function is complete, it is pushed off the stack, moving the control of execution down the stack until the code in the global execution context is complete.

2.2.3 Recovery phase

Each function execution context and global execution context will have a garbage collection, the process of freeing memory space, this process depends on the environment of js code is different, the timing of the collection is also different.

2.3 Lexical environment and variable environment

In ES5, the variable object VO and the active object AO are replaced with LexicalEnvironment Component and VariableEnvironment Component. So the creation phase of the execution context involves three things: determining the this reference, creating the lexical environment, and creating the variable environment

2.3.1 Lexical environment

Lexical Environment is a canonical type that defines the associations of identifiers to specific variables and functions based on Lexical nested structures of ECMAScript code. A lexical environment consists of an environment record and an external lexical environment that may be null.

A lexical environment is essentially a structure that contains a mapping of identifier variables. An identifier represents the name of a variable/function, and a variable is a reference to an actual object (including a function type object) or the original value.

GlobalExectionContext = {  // Global execution context
  LexicalEnvironment: {         // lexical context
    EnvironmentRecord: {       // Environment record
      Type: "Object".// Global environment
      // The identifier is bound here
      outer: <null>           // A reference to the external environment
  }  
}


FunctionExectionContext = { // Function execution context
  LexicalEnvironment: {     // lexical context
    EnvironmentRecord: {    // Environment record
      Type: "Declarative".// Function environment
      // The identifier is bound here // a reference to the external environment
      outer: <Global or outer function environment reference>}}Copy the code

It can be seen from the above that there are two types of lexical environment:

  • Global environment: in the context of global execution, it is a lexical environment with no external environment. The external environment of the global environment is referred to as NULL because it is itself the outermost environment. It has a global object with its associated methods and properties and any user-defined global variables.
  • Function environment: It contains the properties and methods defined by the user in the function, as well as a arguments object. The external context of a functional lexical environment can be global or the context of other functions.

The lexical environment also has two components:

  • 1.EnvironmentRecord: The actual location where variable and function declarations are stored. There are also two types of environmental records:
    • Use object environment loggers in the global context to define the relationship between variables and functions that appear in the global context.
    • 2, in the function environment to use the declaration environment recorder, used to store variable functions, parameters.
  • 2,Reference to the external environment (outer): It can access its external lexical environment.
    • Create lexical environment for global context using object environment logger outer value null
    • Outer is either a global object or a parent lexical environment

2.3.2 Variable Environment

In fact, the variable environment is also lexical environment, it has all the properties of the above lexical environment.

In ES6, the difference between lexical and variable environments is that lexical is used to store function declarations and variables bound with the let and const keywords, whereas variable environments are only used to store variables declared with var.

When we declare global variables using let, const, and var, the global variables declared by let or const are bound to Script objects instead of Window objects, and the global variables declared by var are bound to Window objects. Local variables declared by let,const, or var are bound to Local objects. Script object, Window object, Local object they are parallel.

let num1 = 10;
const num2 = 20;
var num3 = 30;
function fn(g) {
    console.log(num1);
    console.log(g);
    let num1 = 40;
    console.log(num1);
    console.log(g);
}
fn(20);
console.log(num1);
Copy the code

When fn(20) is executed, the function execution context is created:

GlobalExectionContext = {
    ThisBinding: <Global Object>, //this points to LexicalEnvironment: {// lexical EnvironmentRecord: {// environmental record Type: Num1: < uninitialized >, num2: < uninitialized >, fn: < func >} outer: <null> // reference to external environment}, VariableEnvironment: {// variable EnvironmentRecord: {// EnvironmentRecord Type: "Object", // identifier binding here num3: undefined, } outer: <null> } } FunctionExectionContext = { ThisBinding: <Global Object>, LexicalEnvironment: Num1 :<uninitialized>, Arguments: {0: 20, length: },}, outer: <GlobalLexicalEnvironment>}, VariableEnvironment: {// EnvironmentRecord: {Type: Outer: <GlobalLexicalEnvironment>}}Copy the code

Note that num1 and num2, declared with let and const, are stored in the LexicalEnvironment of the GlobalExectionContext, and their values are uninitialized. There is no value associated with it, and the VariableEnvironment holds num3 declared with var, which has a value of undefined. During the creation phase, the code is scanned from top to bottom by the environment and parses variable and function declarations, which are stored in the environment and are set to undefined(in the case of var) or uninitialized (in the case of let/const).

The execution steps are as follows:

  • Start by creating the lexical environment for the global context
    • Create the object environment logger first (to place variables and function declarations and references declared by let,const)
    • Create a reference to the external environment outer (null)
  • Then create the variable environment for the global context,
    • Store the variables declared by var
    • Then create the corresponding object environment logger and a reference to the outer environment
  • Make sure this points to (in the browser this is window)
  • When a function is called: create a private context for the function and push it into the execution environment stack
    • Create the lexical environment for the function context
      • The first is the declarative environment logger, where variables declared with let,const are placed in an uninitialized state.
      • There is also a Arguments object in the lexical environment
      • Finally, its external environment refers to outer(like a chain of scopes)
    • Create the variable environment for the function context
      • Variables declared with var are placed there
      • The second is the reference to the external environment
    • Make sure this points to
    • Code execution is executed on a stack
  • The context is reclaimed after execution
let num1 = 10;
const num2 = 20;
var num3 = 30;
function fn(g) {
    // console.log(num1); //Cannot access 'num1' before initialization
    let num1 = 40;
    console.log(num1) / / 40
}
fn(20);
console.log(num1);/ / 10
Copy the code

Third, the closure

3.1 define

Different people have different understandings of closures from different perspectives, and there are several authoritative explanations as follows:

Closures occur when functions can remember and access all lexical scopes. Even if the function is executed outside the current scope. JavaScript you Don’t Know (Volume 1)

Closures are functions that refer to variables in the scope of another function, usually implemented in nested functions. Advanced JavaScript Programming (part 4)

A combination of a function bound to (or surrounded by) references to its surrounding state (lexical environment) is a closure. That is, closures allow you to access the scope of an outer function within an inner function. – the MDN

Browser to load the page, in my opinion, will form a stack – execution environment for code execution environment, execute when the function into the stack will form a private context – function execution context, this context will protect the private variable undisturbed inside, and if some content in the current context is occupied by a context from the content of the outside, The current context is not released from the stack, so variables and values inside can be saved. So closures can be thought of as a mechanism. A private context created by the execution of a function can protect its private variables from external interference, prevent global variable contamination and store the corresponding values of the private variables for context access. This mechanism is called closure. It is not just those contexts that are not destroyed that are closures.

Example:

var x = 10;
function fn() {
    var x = 30;
    let y = 20;
    return function () {
        z = x + y;
        console.log(z);
    };
}
let f = fn()
f();
Copy the code

General function of the code execution after the browser will automatically make private context for release, but if the current context a and its associated content is occupied by a outside the current context, so the private context can’t stack release, so the private in the context of private variables and values are stored.

In the sample code, we can see that the private variable x in the fn function does not conflict with x in the external global context. The context in which fn is located is not immediately released from the stack, so some information in the context is saved for use by f functions outside the context.

3.2 Application of closures

Closures are widely used, and there are two common applications

  • 1. Take one function as the return value of another function
function fn1() {
    var a = 2;
    function fn2() {
        a++;
        console.log(a);
    }
    return fn2;
}
var f = fn1(); // Execute the external fn1 function, which returns fn2
f(); / / fn2 execution
Copy the code
  • 2. Pass the function as an argument to another function call
function fn(v, t) {
    setTimeout(function () {
        alert(a)
    }, t)
}
fn(100.1000);
Copy the code

3.4 Advantages and disadvantages

Closure protected and preserved the advantage of its mechanism, also the lack of closure is protected and preserved from it, because it will produce a large number of context is destroyed, it will lead to excessive memory consumption, affect the overall performance of the page, so suggest when it is necessary to use, although the v8 engine its efforts to recycle trapped by the closure of memory, but its recovery is still limited, So be careful with closures.

3.4 other

What if we need to expose values in closures? There are the following methods:

  • Global exposure based on window. XXX = XXX

We can see that while this approach does the trick, if we expose every method to the window, we may cause conflicts between global methods or even modify the original global method.

(
    function fn() {
        function fn1() {
            console.log('fn1')}function fn2() {
            / /...
        }
        function fn3() {
            / /...
        }
        window.fn1 = fn1;
        window.fn2 = fn2;
        window.fn3 = fn3;
    }
)();
fn1();
Copy the code
  • Utils exposes methods that need to be common based on returns
var utils = (function fn() {
    function fn1() {
        console.log('fn1')}function fn2() {
        // do something
    }
    // Assign variables and methods that need to be accessed externally to an object, and return
    return {
        fn1: fn1,
        fn2: fn2
    }; //=>return AAAFFF000;}) ();console.log(utils);
utils.fn1();
Copy the code