Execution context

concept

The context in which JS code is executed. Whenever JS code is executed, it is executed in the context of execution.

classification

  1. Global execution context: This is the default execution context in which all code that is not in a function is executed globally. The global execution context does two things :(1) creates a window object (in the browser). (2) Point this to the window object. Note that there is only one global execution context in a program.
  2. Function execution context: A function execution context is created each time a function is called. There can be many function execution contexts.
  3. Eval execution context: The Eval function evaluates a string and executes the JS code in it. There is a security issue, and you should never use Eval if the code string is unknown or comes from a user input source.

For example,

Var sayHello = 'Hello'; Function person () {var firstName = 'xiao'; var lastName = 'ming'; Function getFirst() {return firstName; } function getLast() {return lastName; } return sayHello + getFirst() + getLast(); } person(); // Create the function execution context 'Copy the code

Execute the context life cycle

  1. Creation phase: Three things are done during the creation phase: the this binding, the lexical environment is created, and the variable environment is created. Can be expressed as follows

    ExecutionContext = {ThisBinding = <this value>,// Bind this LexicalEnvironment = {... },// create lexical environment VariableEnvironment = {... },// create variable environment}Copy the code

    (1) The this binding, in the context of global execution this refers to the window object. In the context of function execution, the direction of this depends on how the function is called. If the function is called globally, this refers to window or undefined(in strict mode). If the function is called from another object, this refers to that object.

    var word = 'hello window'; var obj = { word: 'hello', sayHello: function() { console.log(this.word); }}; Obj.sayhello () // sayHello is called by obj so this points to obj, so print 'hello'. var temp = obj.sayHello; // Copy the sayHello pointer to temp. temp(); // Call sayHello globally, so this points to window and prints' hello window';Copy the code

    (2) Create the lexical environment, which is used to register variable declarations, function declarations, function declarations parameters. Later code execution will know where to fetch the value and function of the variable. The lexical environment consists of two parts: the environmental record and the reference to the external lexical environment.

    Context record: The place where variable and function declarations are stored.

    (1) Declarative environment record: Declarative environment record is also special, it only records non-var declaration identifier, such as let, const, function… Declared identifiers and so on. And it has no binding object associated with it. All declared identifiers (which should include var declared identifiers, but do not establish associations) are located here. All non-var declared identifiers are instantiated but not initialized, i.e. variables are in the uninitialized state. That is, the memory has been reserved for variables, but the corresponding identifier has not been bound. It is actually initialized and assigned undefined by default during the Perform state phase of the execution context and when the statement is executed. So you can see that the identifier declared by the let was previously inaccessible because the binding had not been established. That’s the root cause of the temporary dead zone.

    (2) Object-based environment record: Object-based record is also used to record the mapping between identifiers and variables, but it only records the identifiers declared by var; And it has a binding object associated with it. In a lexical environment, all identifiers in the object environment record are bound to the property of the same name of the binding object. Such as var number = 1000; You can also get the value of number in the window.number format.

    Simply put, in a global environment, the environment logger is the object environment logger. In a functional environment, the environment logger is the declarative environment logger.

    References to the external lexical environment: With this reference, you can access variables and functions stored in the parent lexical environment. It is the key to the chain of scopes being connected in series.

(3) Create a variable environment. The variable environment is also a lexical environment, so it has all the properties of the lexical environment defined above. One difference between a lexical environment component and a variable environment is that the former is used to store function declarations and variable bindings (let and const), while the latter is used only to store var variables and function declarations.

For example:

let a = 1;
const b = 2;
var c = 3;
function test (d, e) {
    var f = 10;
    return f * d * e;
}
c = test(a, b);
Copy the code

The above code creates the following execution context:

ThisBinding: <Global Object>,// Bind this LexicalEnvironment: EnvironmentRecord: {Type: "Object", // Bind identifiers a: < uninitialized >, b: < uninitialized >,} outer: <null>}, VariableEnvironment: {// var declaration of variables and function declarations in EnvironmentRecord: {Type: C: undefined, test: <func>,} outer: <null>}} FunctionExectionContext = {// Function execution context, note that the function execution context will only be created when test is called: <Global Object>, LexicalEnvironment: {EnvironmentRecord: {Type: "Declarative", // 2, length: 2}, }, outer: <GlobalLexicalEnvironment> }, VariableEnvironment: { EnvironmentRecord: { Type: Outer: <GlobalLexicalEnvironment>}}Copy the code

You may have noticed that the let and const variables are not associated with any value, but the var variable is set to undefined. This is because during the creation phase, the engine checks the code for variables and function declarations declared by var. Variables declared by let and const are not promoted. This is why you can access variables defined by var (undefined) before declaration, but access variables defined by let and const before declaration will get a reference error.

  1. Execution phase

Code begins the phase that runs in the execution context formed during the creation phase, assigning variable values line by line. At the start of execution, the JS engine looks for a reference to the executing function in its creation phase object. If it cannot be found in its own scope, it will continue to look up until it reaches the global environment.

If no reference is found in the global environment, it returns an error. However, if a reference is found and the function executes correctly, the execution context for that particular function pops off the stack, the JS engine moves on to the next function, where their execution context is added to the stack and executed, and so on.

Let’s look at the above two phases with examples to better understand it. During the creation phase, the global execution context looks something like this

ThisBinding: <Global Object>,// Bind this LexicalEnvironment: EnvironmentRecord: {Type: "Object", // Bind identifiers a: < uninitialized >, b: < uninitialized >,} outer: <null>}, VariableEnvironment: {// var declaration of variables and function declarations in EnvironmentRecord: {Type: Test: <func>,} outer: <null>}}Copy the code

Variables defined by let (a) and const (b) do not have any associated values at the creation stage, but variables defined by var (c) are set to undefined. But when you access let and const variables before they are declared, you get a reference error. This is what we call variable promotion, where all variable declarations that use VAR are raised either by their local scope (declared inside the function) or by the top of their global scope (declared outside the function). In the execution phase, variable assignments are completed. So the global execution context at execution time looks like this:

ThisBinding: <Global Object>,// Bind this LexicalEnvironment: EnvironmentRecord: {Type: "Object", // bind identifiers a: 1, b: 2,} outer: EnvironmentRecord: {Type: "Object", // bind identifiers c: 3, test: <func>, } outer: <null> } }Copy the code
  1. Recovery phase

The execution context goes out of the stack and waits for the VM to reclaim the execution context.

Execution stack

Usually, we have more than one context in our code. What is the order in which these contexts should be executed? Stack is a data structure with the principle of first in, last out. The execution stack in JS has such a structure. When the engine encounters JS code for the first time, it will generate a global execution context and push it into the execution stack. Every time it encounters a function call, it will push a new context into the stack. The engine executes the function at the top of the stack. When the execution is complete, the current execution context pops up. Look at the following examples:

var sayHello = 'Hello'; Function person () {// Function execution context console.log(1); var firstName = 'xiao'; var lastName = 'ming'; Function getFirst() {// Function execution context console.log(2); return firstName; } function getLast() {console.log(3) return lastName; } let first = getFirst(); let second = getLast(); return sayHello + first + second; } person(); // Create the function execution contextCopy the code

Take an example to illustrate. When the person() function is called, push the execution context of the person function onto the stack, and then execute the output ‘1’; When getFirst() is called, the execution context of getFirst is pushed onto the stack, and output ‘2’ is executed. When getFirst() is finished, it is ejected from the stack, the person() function is executed, and getLast() is called, printing ‘3’; The person() function then executes and is popped off the stack.