When learning about how javascript works, there are several concepts that crop up in various articles and can be confusing. Execution Context, Execution Stack, Variable Object(VO), Active Object(AO) Active objects, LexicalEnvironment, VariableEnvironment, especially VO,AO, and LexicalEnvironment, The differences between variableEnvironments are not covered in many articles. Therefore, I checked some articles at home and abroad and wrote down the following notes based on my own understanding. Although there is a bias due to my own shortcomings, I still believe that reading the following will be helpful for understanding javascript concepts such as variable promotion, scope and closure.

First, the execution environment and execution stack

To understand how javascript works, you must first understand two basic concepts. Execution Context and Context Stack

What is the Execution Context?

We know that javascript is a single-threaded language, meaning that only one task can be performed at a time. When the javascript interpreter initializes the code, it enters the global execution environment by default, and creates a new execution environment for each subsequent function call.

var a = 1; // 1. Initialize the global execution environment by defaultfunction b() {// 3. Enter the execution environment of Bfunction c() {// 5. Enter the execution environment of C ···} c() // 4. } b() // 2. Call b to create b's execution environmentCopy the code

Classification of execution environment:

  • Global execution environment: A program has only one global object, namely the Window object, and the execution environment of the global object is the global execution environment.
  • Function execution environment: The process of calling a function creates a function execution environment, so each program can have an infinite number of function execution environments.
  • Eval Execution context: The context in which Eval code is specified.

2. How to run a single thread (Context Stack)

Let’s start with a simple example

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

How to store the execution environment (global execution environment, function execution environment) of the code runtime is the execution stack. Stack and follows the principle of advanced out after, after initialization javascript code, global execution environment is created first and push the execution of the current stack, when calling a function, javascript engine creates a new execution environment and push the top of the stack to the current execution, in the new execution environment, if continue to happen when a new function call, Continue to create a new execution environment and push it to the top of the current execution stack until there are no more new function calls. When the uppermost function completes execution, its execution environment pops up from the current stack and passes control to the next execution environment on the current stack, up to the global execution environment. When the program or browser closes, the global environment exits and is destroyed.

So the output is:

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

3. How do I create an execution environment

We now know that each time a function is called, the javascript engine creates a new execution environment. How to create this sequence of execution environments is that the executor is divided into two phases: the creation phase and the activation (execution) phase. Even though the steps are the same, the process performed at each stage is quite different due to different specifications.

3.1 ES3 specification

Creation phase:

  • 1. Create a scope chain.
  • 2. Create variable object VO(including parameters, functions, and variables).
  • 3. Determine the value of this.

Activation/Execution phase:

  • Complete variable assignment and execute code.
3.2 ES5 specification

Creation phase:

  • 1. Determine the value of this.
  • 2. Create LexicalEnvironment.
  • 3. Create a VariableEnvironment.

Activation/Execution phase:

  • Complete variable assignment and execute code.

As we know from the specification, ES3 and ES5 differ during the creation phase of the execution environment. Of course, they both determine the value of this at this stage. We will focus on these two specification differences. Although some of the ES3 specifications have been discarded, understanding the process of creating an execution environment in ES3 still helps us understand javascript’s deeper concepts.

Variable Object(VO), Active Object(AO)

2.1 Basic Concepts

VO and AO are concepts in the ES3 specification. We know that variable objects are created in the second stage of the creation process, namely VO, which is used to store function identifiers, parameters, variable declarations, etc. that can be accessed in the execution environment but cannot be deleted. This object is not accessible in the JS environment. The difference between AO and VO is that AO is an active VO, that’s all.

  • Variable object refers to the JS execution context has an object to store the execution context can be accessed but cannot be deleted function identifiers, parameters, Variable declarations, etc. They’re going to be attached to this object, 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 not technically or the engine implementation is not accessible to live objects in the JS environment

  • An Activation object has a variable object that stores things in each context, but when can it be accessed? Each time an execution context is entered, the variable object in the execution context is activated, that is, function identifiers, parameters, variable declarations, etc. in the context can be accessed

2.2 Execution Details

How to create VO objects can be roughly divided into four steps

  • Create arguments objects
  • 2. Scan the function declaration (not the function expression) of the context, and for each function found, create an attribute on the variable object — the function name, to be exact — that has an in-memory reference to the function. If the function name already exists, the reference pointer will be overridden.
  • 3. Scan the variable declarations of the context. For each variable declaration found, create an attribute on the variable object — the name of the variable, and initialize the value of the variable to undefined. If the variable name already exists in the variable object, no action is taken and the scan continues.

Note: The whole process can be roughly described as: function parameter => function declaration => variable declaration, where the name of the function declaration is overridden when it is created, and the name of the variable name is ignored when it is created.

A simple example

function foo(i) {
    var a = 'hello';
    var b = function privateB() {};function c() {

    }
}

foo(22);
Copy the code

Pseudocode to execute

// fooExecutionContext = {scopeChain: {... }, variableObject: { arguments: { 0: 22, length: 1 }, i: 22, c: pointer tofunctionc() a: undefined, b: undefined }, this: { ... }} // Activation phase fooExecutionContext = {scopeChain: {... }, variableObject: { arguments: { 0: 22, length: 1 }, i: 22, c: pointer tofunction c()
        a: 'hello',
        b: pointer to functionprivateB() }, this: { ... }}Copy the code

3, LexicalEnvironment, VariableEnvironment

3.1 Basic Concepts

Lexical environment and variable environment are concepts mentioned later in ES5. The official explanation of lexical environment is as follows.

A lexical environment is a canonical type that defines the associations of identifiers with specific variables and functions based on the lexical nesting structure of ECMAScript code. A lexical environment consists of an environment record and an external lexical environment that may be null.

To put it simply, a lexical environment is a structure that contains a mapping of identifier variables. The identifier here represents the name of a variable/function, which is a reference to an actual object (including a function type object) or the original value.

Why can VO and AO of ES3 be discarded? I think there are two reasons. The first is that the creation of scope chains and creation of variable objects (VO) performed during creation can be done during creation of lexical environment. The second is the binding of stored function declarations and variables (let and const) and stored variables (var) in ES6, which can be distinguished by two different procedures (lexical environment, variable environment).

3.2 lexicalEnvironment

The lexical environment consists of two parts

  • Enviroment Records, which store variable and function declarations
  • A reference to the external environment (outer) through which the external lexical environment can be accessed

The reference to the external environment relates to the scope chain, and after analysis, let’s first look at the classification of environment records.

The environmental record is divided into two parts

  • Declarative Environment Records: Stores variables, functions, and parameters, but mainly for function and catch lexical environments. Note: The values of arguments are stored in a function environment. The detailed process can refer to the implementation details of VO, which are basically similar with minor differences
  • Object Environment Records, mainly for with and global lexical environments

The pseudocode is as follows

GlobalExectionContext = {// LexicalEnvironment: {EnvironmentRecord: {···} outer: <null>}} // FunctionExectionContext = {// LexicalEnvironment: {EnvironmentRecord: {// contains argument} outer: <Global or outerfunction environment reference>  
  }  
}
Copy the code

3.3 Variable Environment (objectEnvironment)

The variable environment is also a lexical environment, the main difference being that lexicalEnviroment is used to store function declarations and variable (let and const) bindings, while ObjectEnviroment is only used to store variable (var) bindings.

3.4 Pseudo-code display

The entire creation process under the ES5 specification can be seen in the pseudocode below

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

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

c = d(20, 30);
Copy the code
// GlobalExectionContext = {this: <Global Object>, // LexicalEnvironment: {EnvironmentRecord: {Type:"Object"A: < uninitialized >, // Uninitialized B: < uninitialized >, d: < func >} outer: <null> }, VariableEnvironment: { EnvironmentRecord: { Type:"Object"C: undefined, // undefined} outer: <null>}} // FunctionExectionContext = {this: <Global Object>, LexicalEnvironment: { EnvironmentRecord: { Type:"Declarative"Arguments: {0: 20, 1: 30, length: 2}, outer: Arguments: {0: 20, 1: 30, length: 2} <GlobalLexicalEnvironment> }, VariableEnvironment: { EnvironmentRecord: { Type:"Declarative"G: undefined}, outer: <GlobalLexicalEnvironment>}}Copy the code

4. Scope chain

In the creation process, we left a hint that the ES3 specification has a procedure for creating scope chains, while ES5 has a procedure for generating references to external environments while creating lexical or variable environments. So what does this process do? Let’s illustrate this with a simple example.

function one() {

    var a = 1;
    two();

    function two() {

        var b = 2;
        three();

        function three() {

            var c = 3;
            alert(a + b + c); // 6

        }

    }

}

one();
Copy the code

When the execution reaches the execution environment of three, a and B are not in the variable of C, so the scope chain plays a role of referencing the external execution environment variable. The scope chain created in ES3 is shown below:

When the interpreter executes alert(a + b + c), it first looks for the presence of the variable a in its execution environment, and if not, it looks at the scope chain to determine whether a is inside the previous execution environment. It checks to see if A exists internally, and if it doesn’t, it goes up the scope chain one execution environment until it finds it, or to the top-level global scope. The same can be done in the ES6 specification.

So this is going to introduce javascript to an important concept, closures. A closure is a variable that the inner environment accesses to the upper environment through a chain of scopes. There is also the problem of not being able to recycle variables, as long as the function’s scope chain is there, the value of the variable cannot be recycled because of the closure.

Note: This scope chain is not the same concept as the scope chain of the prototype chain.

5, summary

Through the introduction of javascript operation mechanism, some javasript advanced concepts have a deeper understanding, especially for some of the concepts in the fog of the difference has a deeper understanding. Under different specifications, the interpretation of different concepts is more conducive to digging into the underlying execution ideas of javascript. I believe this is the most important step in understanding the javascript language.

References:

  • Stackoverflow.com/questions/2…
  • Davidshariff.com/blog/what-i…
  • Davidshariff.com/blog/javasc…
  • Github.com/yued-fe/y-t…
  • Segmentfault.com/a/119000000…

This article is the blogger original article, reproduced please indicate the source juejin.cn/post/684490…