As we all know, the order in which JS code is executed is always different from the order in which the code is executed. If you put aside the asynchronous problem, you will find that even synchronous code does not execute as you expected. For example:

Function f1() {console.log(' listen to wind is wind '); }; f1(); //echo function f1() { console.log('echo'); }; f1(); //echoCopy the code

Echo = echo = echo = echo = echo = echo = echo = echo = echo If we change the function declaration in the above code to a function expression, the result is different again:

Var f1 = function () {console.log(' listen to wind is wind '); }; f1(); Var f1 = function() {console.log('echo'); }; f1(); //echoCopy the code

This means that the code must have changed subtly before execution. What does the JS engine actually do? This brings us to the JS execution context.

JS code before execution, JS engine always need to do some preparatory work, this work is actually to create the corresponding execution context;

There are only three types of execution contexts: global execution context, function context, and eval context; Since eval is not generally used, we will not discuss it here.

There is only one global execution context, usually created by the browser on the client side, known as the Window object, which we can access directly through this.

The global object Window has a large number of predefined methods and properties that can be accessed directly anywhere in the global environment, and the Window object is also the carrier of the global variables declared by var. Any global object we create with var can be accessed directly through window.

There can be an infinite number of function execution contexts, one being created each time a function is called; Note that multiple calls to the same function create a new context.

If you’re wondering what the relationship is between the different types of contexts and the number of contexts that are created, and who manages those contexts, then you have to talk about implementing the context stack.

The execution context stack (hereinafter referred to as the execution stack) is also called the call stack. The execution stack is used to store all the contexts created during the execution of the code and has the feature OF LIFO (Last In First Out).

When the JS code runs for the first time, it creates a global execution context and pushes it into the execution stack. After that, every time a function is called, a new function execution context will be created and pushed into the stack. Due to the nature of the execution stack LIFO, it can be understood that there is always a global execution context at the bottom of the execution stack before the JS code completes execution.

function f1() { f2(); console.log(1); }; function f2() { f3(); console.log(2); }; function f3() { console.log(3); }; f1(); / / 3 2 1Copy the code

We explain the execution process of the above code through the relationship between the execution stack and the context. For easy understanding, we pretend that the execution stack is an array, and the global execution context must be created and pushed at the beginning of the code execution, so the process is roughly as follows:

ECStack = [globalContext]; // f1 calls ecstack.push ('f1 functionContext'); // f1 calls f2 again, and f2 cannot console 1 ecstack. push('f2 functionContext'); // f2 calls f3, and f3 cannot console 2 ecstack. push('f3 functionContext') until it is finished; Ecstack.pop (); // f2 complete, output 2 and exit stack ecstack.pop (); Ecstack.pop (); // There is only one global execution context left in the stackCopy the code

So here, we explained the execution stack and execution context storage rules; Remember I mentioned earlier that the JS engine prepares to create an execution context before the code executes, and how to do that? Let’s move on.

Execution context creation is divided into two phases: the creation phase and the execution phase.

The creation phase of the JS execution context is responsible for three things: determining this– creating LexicalEnvironment components — creating VariableEnvironment components

Here I directly refer to the pseudo-code translated by others to represent the creation process:

ThisBinding = <this value>, // create LexicalEnvironment component = {}, // create the VariableEnvironment component VariableEnvironment = {},};Copy the code

If you’ve been reading other articles about execution context, you might be wondering, shouldn’t the execution context creation process explain this, scope and variable object/active object differently from other places, and I’ll explain that later.

The official name is This Binding. In a global execution context, This always points to a global object, such as a window object in a browser context.

In the context of function execution, the value of this depends on how the function is called; if called by an object, this refers to that object. Otherwise, this usually refers to the global object Window or undefined (strict mode).

2. Lexical environment components

A lexical environment is a structure that contains a mapping of identifiers that represent the names of variables/functions. Variables are references to actual objects (including function-type objects) or original values.

Lexical environment consists of two parts: environmental record and introduction record to external environment.

Where the environment record is used to store the actual location of variable and function declarations in the current environment; It’s easy to understand that the external environment introduces records, which are used to store other external environments that their own environment can access, so by talking about this, is it a bit of a scope chain?

We mentioned the global execution context and the function execution context earlier, so this also leads to the lexical environment into the global lexical environment and the function lexical environment.

Global lexical environment components:

The incoming record for the external environment is null because it is the outermost environment itself, and it also records all the attributes and method positions in the current environment.

Functional lexical environment components:

Contains all property methods defined by the user in a function, plus a arguments object. The external context of a functional lexical environment can be global or other functional environment, depending on the actual code.

The global environment record is called the object environment record, and the function environment record is called the declarative environment record.

// GlobalExectionContext = {// LexicalEnvironment: {// EnvironmentRecord: {Type: }, outer: < null >}}; // FunctionExectionContext = {// function LexicalEnvironment LexicalEnvironment: {// EnvironmentRecord: {Type: Outer: < Global or outerfunction environment reference >}};Copy the code

3. Variable environment components

Variable environment can be said to be lexical environment, it has all the attributes of lexical environment, as well as environmental record and external environment introduction. The only difference in ES6 is that the lexical environment is used to store variables declared by functions and let const, whereas the variable environment only stores variables declared by var.

We understand them through a string of pseudocode:

let a = 20;  
const b = 30;  
var c;

function multiply(e, f) {  
 var g = 20;  
 return e * f * g;  
}

c = multiply(20, 30);
Copy the code

We use pseudocode to describe the creation of an execution context in the above code:

ThisBinding: <Global Object>, // LexicalEnvironment: // EnvironmentRecord: {Type: "Object", // EnvironmentRecord: {Type: "Object", // EnvironmentRecord: {// EnvironmentRecord: {Type: "Object", // EnvironmentRecord: {// EnvironmentRecord: {Type: "Object", // EnvironmentRecord: {// EnvironmentRecord: {Type: "Object", // < uninitialized >, multiply: < func >} // Global environment external environment is imported as null outer: <null>}, VariableEnvironment: {EnvironmentRecord: {Type: "Object", // Object environment record // identifier binding in here var created c in here c: undefined,} // global environment external environment introduced as null outer: <null>}} // Function execution context FunctionExectionContext = {// Since the function is called by default this binding is also the global object ThisBinding: <Global Object>, // LexicalEnvironment: {EnvironmentRecord: {Type: "Declarative", // Declarative environment record // identifier binding here Arguments object here: {0:20, 1:30, length: </Global> outer: <GlobalEnvironment>}, VariableEnvironment: {EnvironmentRecord: {Type: </Global> outer: <GlobalEnvironment>}} </Global> outer: <GlobalEnvironment>}Copy the code

The var declaration is set to undefined, the function is set to function, and the let const is set to uninitialized.

Now you know how variable promotion and function declaration advance work, and why let const has a temporary dead field. It’s because the JS engine initialized them differently during scope-creation.

In addition to the creation stage, there is also the execution stage, which should be easy to understand. When the code is executed, it will be assigned according to the previous environment record. For example, in the early stage, var is undefined in the creation stage, and if there is a value, it will be assigned.

About variable objects and active objects

To answer the previous question, why do other blog posts talk about scopes, variable objects, and active objects in the context of their introduction, I have become lexical environment, variable environment.

This question also occurred when I was reading relevant materials. After checking it, I can confirm that the concept of variable object and active object is an old concept proposed in ES3, which has been replaced by lexical environment and variable environment since ES5, because it is easier to explain.

In the previous article, we explained why var has a variable boost and why let const does not by introducing lexical and variable environments. Variable objects and live objects are difficult to explain because JavaScript is constantly plugging holes in its design in updates.

Second, the concept of lexical environment can also correspond to concepts such as variable objects.

We know that both variable objects and active objects are actually variable objects. Variable objects are data scopes that are specific to the execution context and store variable and function declarations defined in the context. In the functional context, we refer to a variable object as an activation object (AO).

This corresponds to global lexical record and functional lexical record. And since ES6’s new let const does not have variable promotion, the concept of lexical and variable environments is introduced to explain the problem.

So at this point, you don’t have to mess with the concept of lexical context, variable object.

Let’s summarize the concepts mentioned above.

❀ summarize lu

1. The global execution context is typically created by the browser and is created when the code executes; Function execution contexts are created only when the function is called, and as many times as the function is called.

2. The call stack is used to store all execution contexts and meets FILO rules.

3. The execution context creation stage is divided into binding this, creating lexical environment and variable environment. The difference between the two is that lexical environment stores variables declared by function and const let, while variable environment only stores variables declared by var.

4. The lexical environment is mainly composed of two parts: the environment record and the external environment record. The external environment record of the global context is different from the function context, the global is null, the function is the global environment or other function environment. The environment record is also different. The global is called object environment record, and the function is called declarative environment record.

5. You can see why variables are promoted, functions are promoted, and let const is not.

6. The concepts of variable object and active object before ES3 are explained by lexical environment and variable environment after ES5. There is no conflict between the two concepts, and the latter is easier to understand.

Have to say that the relevant article is also to see my heart tired, but also hope to be predestined to help you, so here, the end of this article.

reference

Understand the execution context and execution stack in JavaScript

Understand Javascript execution context and execution stack

JavaScript deep execution context stack

JavaScript deep variable objects

What is the difference between active objects and variable objects in JS?