Coding With Fun
Home Docker Django Node.js Articles FAQ

(ES5 version) in-depth understanding of JavaScript execution context and execution stack


May 31, 2021 Article blog


Table of contents


The article comes from the public number: front-end Taoyuan, author Tao Ong

I've been studying JavaScript foundational stuff recently, but seeing the interpretation of the execution context, I've found that there are two kinds of execution contexts: scope variable object (variable object), this value ( this value), and one that contains: lexical environment variable environment (variable environment), and this value

Later I read a lot of blogs and ES3 and ES5 specifications to understand that the first is the ES3 specification, the classic book "JavaScript Advanced Program Design" the third edition is explained in this way, is also widely circulated on the Internet one, the other is the ES5 specification.

Then I went on to turn over ES2018, found that there has been a change, has added more content, considering this part of the content is quite complex, ready to be followed by a summary to share, check the information to see this talk about the execution context (ES5) is good, so translate it out and share it with everyone first.

Later see variable objects, active objects know is the content of ES3, and if it is lexical environment, variable environment this word is the content after ES5.

Here's the body:

What is the execution context?

In short, execution context is an abstract concept of the environment in which JavaScript code is calculated and executed. Whenever JavaScript code is running, it runs in the execution context.

The type of execution context

There are three execution context types in JavaScript.

  • Global execution context - This is the default or underlying context, and any code that is not inside the function is in the global context. I t does two things: create a global window object (in the case of a browser) and set the value of this to equal the global object. There is only one global execution context in a program.
  • Function execution context - Creates a new context for a function whenever it is called. E ach function has its own execution context, but is created when the function is called. A function context can have as many as possible. Whenever a new execution context is created, it performs a series of steps in the defined order, which will be discussed later.
  • Eval function execution context - The code that executes inside the eval function also has its own execution context, but since JavaScript developers don't use eval very often, I won't discuss it here.

The execution stack

The execution stack, also known as the "call stack" in other programming languages, is a data structure that has a LIFO (last in, first out) and is used to store all execution contexts created by the code runtime.

When the JavaScript engine first encounters your script, it creates a global execution context and presses into the current execution stack. Whenever the engine encounters a function call, it creates a new execution context for the function and presses it into the top of the stack.

The engine executes functions in the execution context at the top of the stack. When the function execution ends, the execution context pops up from the stack and the control process reaches the next context in the current stack.

Let's understand this through the following code example:

let a = 'Hello World!';


functionfirst() {
  console.log('Inside first function');
  second();
  console.log('Again inside first function');
}


functionsecond() {
  console.log('Inside second function');
}


first();
console.log('Inside Global Execution Context');

 (ES5 version) in-depth understanding of JavaScript execution context and execution stack1

When the above code is loaded in the browser, the JavaScript engine creates a global execution context and presses it into the current execution stack. When a first() function call is encountered, the JavaScript engine creates a new execution context for the function and presses it into the top of the current execution stack.

When first() function is called from inside the second() function, the JavaScript engine creates a new execution context for second() function and presses it into the top of the current execution stack. When second() function is executed, its execution context pops up from the current stack and the control process reaches the next execution context, the execution context of the first() function.

When first() is executed, its execution context pops up from the stack and the control process reaches the global execution context. Once all the code is executed, the JavaScript engine removes the global execution context from the current stack.

How do I create an execution context?

Now that we've seen how JavaScript manages execution contexts, let's see how the JavaScript engine creates execution contexts.

There are two stages to creating an execution context: 1) to create and 2) to execute.

The creation phase

The execution context goes through the creation phase before javaScript code is executed. There are three things that happen during the creation phase:

  1. The decision of this value, which is known as this binding.
  2. Create a lexical environment component.
  3. Create a variable environment component.

So the execution context is conceptually like this:

ExecutionContext = {
  ThisBinding = <this value>,
  LexicalEnvironment = { ... },
  VariableEnvironment = { ... },
}

This binding:

In the context of global execution, the value of this points to the global object. (In the browser, this refers to the Window object).

In the context of function execution, the value of this depends on how the function is called. I f it is called by a reference object, this is set to that object, otherwise this value of this is set to a global object or undefined (in strict mode). For example:

let foo = {
  baz: function() {
  console.log(this);
  }
}


foo.baz();   // 'this' 引用 'foo', 因为 'baz' 被
             // 对象 'foo' 调用


let bar = foo.baz;


bar();       // 'this' 指向全局 window 对象,因为
             // 没有指定引用对象

Lexical environment

The official ES6 document defines the lexical environment as

The lexical environment is a canonical type that defines the association of identifiers with specific variables and functions based on the lexical nesting structure of the ECMAScript code. A lexical environment consists of an environmental logger and a possible reference to the empty value of the outer lexical environment.

Simply put, the lexical environment is a structure that holds an identifier-variable map. (The identifier here refers to the name of the variable/function, and the variable is a reference to the actual object , which contains the function type object, or the original data.)

There are now two components inside the lexical environment: (1) an environment logger and (2) a reference to an external environment.

  1. An environment logger is the actual location where variables and function declarations are stored.
  2. A reference to an external environment means that it can access its parent lexical environment (scope).

The external environment is already similar to the scope specified in ES3

There are two types of lexical environments:

  • The global environment (in the context of global execution) is a lexical environment that is not referenced by the external environment. T he external environment reference for the global environment is null. It has built-in Object/Array/et al., prototype functions within the environment logger (associating global objects, such as window objects) and any user-defined global variables, and the value of this points to the global object.
  • In a function environment, user-defined variables within a function are stored in the environment logger. And the referenced external environment may be a global environment, or any external function that contains this inner function.

There are also two types of environmental loggers (e.g. above!) ):

  1. Declarative Environment Logger stores variables, functions, and parameters.
  2. Object Environment Logger is used to define the relationship between variables and functions that appear in the global context.

nutshell

  • In a global environment, an environment logger is an object environment logger.
  • In a function environment, an environment logger is a declarative environment logger.

Note - For a function environment, the declarative environment logger also contains an arguments object passed to the function (which arguments a map of indexes and parameters) and a length of the argument passed to the function.

Abstractly speaking, the lexical environment looks like this in pseudo-code:

GlobalExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // 在这里绑定标识符
    }
    outer: <null>
  }
}


FunctionExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // 在这里绑定标识符
    }
    outer: <Global or outer function environment reference>
  }
}

Variable environment:

It is also a lexical environment whose environment logger holds a binding relationship created in the execution context by a variable declaration statement.

As mentioned above, a variable environment is also a lexical environment, so it has all the properties of the lexical environment defined above.

In ES6, a difference between the lexical environment component and the variable environment is that the former is used to store function declarations and variable let and const bindings, while the latter is used only to store var variable bindings.

Let's look at the sample code to understand the above concept:

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


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


c = multiply(20, 30);

The execution context looks like this:

GlobalExectionContext = {


  ThisBinding: <Global Object>,


  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // 在这里绑定标识符
      a: < uninitialized >,
      b: < uninitialized >,
      multiply: < func >
    }
    outer: <null>
  },


  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // 在这里绑定标识符
      c: undefined,
    }
    outer: <null>
  }
}


FunctionExectionContext = {
  ThisBinding: <Global Object>,


  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // 在这里绑定标识符
      Arguments: {0: 20, 1: 30, length: 2},
    },
    outer: <GlobalLexicalEnvironment>
  },


VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // 在这里绑定标识符
      g: undefined
    },
    outer: <GlobalLexicalEnvironment>
  }
}

Note - The function execution context is created only when the function multiply is encountered.

You may have noticed that let variables defined by let and const are not associated with any values, but the variables defined by var are set to undefined

This is because during the creation phase, the engine checks the code to find variables and function declarations, which are initially set to undefined var of var) or not initialized let case of let and const although the function declarations are stored entirely in the environment.

This is why you can access var variables (although undefined but let and const variables before the declaration results in a reference error.

This is what we mean by variable declaration elevation.

The execution phase

This is the simplest part of the whole article. At this stage, the allocation of all these variables is completed, and the code is executed at the end.

Note - During the execution phase, if the JavaScript engine cannot find the value of the let variable at the actual location declared in the source code, it is assigned a value of undefined

conclusion

We've discussed how JavaScript is executed inside the program. While you don't need to learn all of these concepts to be a great JavaScript developer, having a good understanding of the above concepts will help you understand other concepts more easily and deeply, such as variable declaration elevation, scope, and closure.

That's what W3Cschool编程狮 has learned about (ES5 version) in-depth understanding of JavaScript execution context and execution stack, and I hope it will help you.