The execution context of Javascript

An execution context is an abstraction of the context in which the current Javascript code is being parsed and executed.

The type of execution context

There are three types of execution contexts

  • Global execution contextThere is only one, the global object in the browserwindowObject,thisPoint to this global object
  • A function execution context is created only when the function is called, and a new execution context is created with each call
  • The eval function executes the context

Execution stack

An execution stack, also called a call stack, has a LIFO(LIFO) structure and is used to store all execution contexts created during code execution.

When js code is first run, a global execution context is created and pushed to the current execution stack, and each time a function call occurs, a new function execution context is created for that function and pushed to the top of the current execution stack.

According to the rules of execution stack LIFO, when the execution of the top function is completed, its corresponding function execution context will pop out of the execution stack, and the control of the context will be transferred to the next execution context of the current execution stack.

Execution context

The execution context is divided into two phases: 1. Creation phase 2. Execution phase

The creation phase of the execution context

  1. LexicalEnvironmentThe lexical environment component is created
  2. VariableEnvironmentThe variable environment component is created

Pseudo code:

ExecutionContext = {
  LexicalEnvironment: {... },VariableEnvironment: {... }},Copy the code

LexicalEnvironment

A lexical environment is a structure of identifiers and variable mappings (identifiers are variable/function names, variables are actual objects: references to objects or original values).

For example, the following code:

var a = 20;
var b = 40;
function foo() {
  console.log("bar");
}
Copy the code

So his lexical environment is something like this:

lexicalEnvironment = {
  a: 20.b: 40.foo: <ref. to foo function>
}
Copy the code

The lexical environment consists of three parts:

  1. Context record: The actual location where variable and function declarations are stored
  2. References to the external environment: Access to its external lexical environment
  3. Make sure this is also calledThis Binding

There are two types of lexical environments:

  1. Global context: lexical context without an external context, which is referenced asnullOwning a global object with its associated methods and properties and any user-defined global variables
  2. Function environment: Variables defined by the user in functions are stored inEnvironmental recordsContainsargumentsA reference to an external environment can be a global object or an external function environment that contains an internal function.

This Binding:

  • In the context of global execution,thisThe value points to the global object in the browserthisThe value of the pointwindowObject, while innodejsTo point to this filemoduleObject.
  • Function execution context,thisThe value of “depends on how the function is called: default binding, implicit binding, explicit binding,new binding, arrow function.

Pseudo code:

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
  this: <global object>,} FunctionExectionContext = {// function execution context LexicalEnvironment: {// lexical EnvironmentRecord: {// environment record Type: "Declarative", // function environment // identifier binding here // reference to the external environment}, outer: <Global or outer function environment reference> this: <depends on how function is called>, }Copy the code

VariableEnvironment

The variable environment is also a lexical environment, so it has all the attributes of the lexical environment defined above.

In ES6, the difference between lexical and variable environments is that the former stores function declarations and variable (let and const) bindings, while the latter stores variable (var) bindings only

The execution phase of the execution context

Here’s an example:

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

In the creation phase, the global execution context looks like this:

GlobalExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object".// The identifier is bound here
      a: < uninitialized >,
      b: < uninitialized >,
      multiply: < func >
    }
    outer: <null>,
    this: <Global Object>,}, VariableEnvironment: {EnvironmentRecord: {Type: "Object", // identifier binding c: undefined,} outer: <null>, this: <Global Object>, } }Copy the code

At execution time, the variable assignment is complete, so the global execution context looks like this at execution time:

GlobalExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object".// Identifier bindings go here
      a: 20.b: 30.multiply: < func >
    }
    outer: <null>,
    ThisBinding: <Global Object>
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // Identifier bindings go here
      c: undefined,
    }
    outer: <null>,
    ThisBinding: <Global Object>
  }
}
Copy the code

When multiply(20, 30) is executed, a new function execution context is created to execute the function code, so the function execution context is created as follows:

FunctionExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative".// Identifier bindings go here
      Arguments: {0: 20.1: 30.length: 2}},outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>,
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // Identifier bindings go here
      g: undefined
    },
    outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>
  }
}
Copy the code

The execution context enters the execution phase, and the variables inside the function have been assigned, so the function execution context looks like this:

FunctionExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative".// Identifier bindings go here
      Arguments: {0: 20.1: 30.length: 2}},outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>,
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // Identifier bindings go here
      g: 20
    },
    outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>
  }
}
Copy the code

After the function completes, the return value is stored in C, the global lexical environment is updated, the global code is executed, and the program ends.

Variable promotions and temporary dead zones

We can see this in the execution context. At the creation stage of the execution context, variables declared by let and const do not have any values associated with them, while variables declared by var are set to undefined.

This is because during the creation phase, the code scans the variable and function declarations, while the function declarations are stored entirely in the environment and the variables are set to undefined(var) or left uninitialized (let and const).

This is why variables declared with var can be accessed before declaration (variable promotion), while variables declared with let and const can be accessed before declaration with an error (temporary dead zone).

Variable promotion can happen to both variables and functions, and function declarations take precedence over variable declarations. Here are a few examples of how variable promotion rules work.

Example 1, variable promotion:

foo; // undefined
var foo = function () {
  console.log("foo1");
};

foo(); // foo1, foo is assigned

var foo = function () {
  console.log("foo2");
};

foo(); // reassign foo2, foo
Copy the code

Example 2, function promotion:

foo(); // foo2
function foo() {
  console.log("foo1");
}

foo(); // foo2

function foo() {
  console.log("foo2");
}

foo(); // foo2
Copy the code

In example 3, function declarations take precedence over variable declarations

foo(); // foo2
var foo = function () {
  console.log("foo1");
};

foo(); // reassign foo1, foo

function foo() {
  console.log("foo2");
}

foo(); // foo1
Copy the code

reference

Advanced Front-end – Understand the execution context and execution stack in JavaScript

Sukhjinder Arora – Understanding Execution Context and Execution Stack in Javascript

I welcome exchange and correctionCopy the code