A lot of times we see js context in various sources, so what is context? In JS Advanced Programming, context is a concept. The context of variables or functions determines what they can access and how they behave. Each context has an associated variable object on which all variables and functions defined by the context reside. Although this object cannot be accessed directly by code, it is used to process data in the background. I found a more digestible explanation on Github, which I moved here to see the link for the original article

Execution context

The types of js execution contexts include: global context, function context, and context in eval/with code (eval and with cause scoped side effects and can’t be optimized, so basically we don’t use them).

The global context refers to the outermost context, and the object representing the global context may be different depending on the hosting environment of the ECMAScript implementation. In the browser, the global context is the Window object, so all global variables and functions defined by var become properties and methods of the Window object. The context is destroyed when all of its code has been executed.

Each function call has its own context, when code execution to the function, the function of the context will be pushed to a context stack, after the function performs the context stack will pop up the function context, will return control to the execution context, before the program execution flow is controlled by the context stack.

When we execute a function, we perform preparation, which is the execution context. In practice, the JS engine creates an Execution Context stack (ECS) to manage the Execution context. We can use pseudocode to simulate the execution of a function.

ECStack = []// Define the execution context stack as an array; /** * The stack will be emptied when the whole program is finished. The stack will be emptied when the whole program is finished. Keep globalContext */ ECStack = [globalContext] at the bottom of the stack; * function fun3() {console.log(' console.log ')} * function fun2() {fun3(); } * function fun1() {fun2(); } * fun1(); */ ecstack. push(<fun1>functionContext) // Push its context before executing fun1 ecstack. push(<fun2>functionContext) // push the context of fun2 Ecstack.push (<fun3>functionContext) // Push fun3 context ecstack.pop () //fun3 context is executed, delete ecstack.pop () //fun2 context is executed, Remove the ecstack.pop () //fun1 context, remove the /** * execution context stack then runs to execute the following function, but the globalContext globalContext holds */Copy the code
  • It is important to note that JS uses lexical scope. The scope of a function is determined at the time the function is defined. Scope is the area of a program’s source code where variables are defined. A scope specifies how to find a variable, that is, determine the area of access to the variable by the currently executing code. That is as follows:
var value = 1; function foo() { console.log(value); } function bar() { var value = 2; foo(); } bar(); / / 1Copy the code

We also mentioned earlier that each context has a managed variable object. But there are actually three important properties for each execution context:

  • Variable Object (VO)
  • Scope chain
  • this

The variable object

Let’s start with variable objects. A variable object is an execution context-specific data scope that stores variable and function declarations defined in the context.

A variable object in a global context is actually a global object. The global object in the browser is the Window object, which defines many methods and functions. A global object is an object instantiated by the object constructor.

Function in the context of the variable object, we use the active object to represent the activation (object), my understanding is the variable object is the standard definition, engine implementation of an object, but actually we can’t access in js environment, only when we enter into the function in the context of time, will be activated as active objects. Active objects are created when they enter the function context and are initialized via the function’s Arguments property, whose value is arguments object.

When entering the context, code is processed in two stages: entering the execution context and executing the code. Take the following function for example:

function foo(a) {
  var b = 2;
  function c() {}
  var d = function() {};
  b = 3;
}
foo(1);
Copy the code
  • Enter the execution context (note that parameters/function declarations/variable declarations are created with precedence) variable objects include :(here are actually rules for promoting variables)
    • The arguments to a function
      • Creates a name-value attribute for a variable object, with undefined if there are no arguments
    • Function declaration
      • Creates an attribute of a variable object with the name -function object, replacing the original attribute if the same name exists
    • Variable declarations
      • Creates a property of a variable object whose name is undefined. If the name exists, the previous property is not affected

    The active object is:

    AO = {arguments:{0:1, length:1}, a:1, c:reference to function c(){}, d:undefinded,// function expressions are not enhanced by function declarations b:undefinded}Copy the code
  • Code execution During the code execution phase, the code is executed sequentially, changing the values in the variable objects in turn. After the above function is executed, the active object is:
     AO = {  
        arguments:{
              0:1,
              length:1
        },
        a:1,
        c:reference to function c(){},
        d:refrence to function Expressions "d",
        b:3
    }
    Copy the code

The scope chain

When a variable is searched, it is first searched from the variable object of the current context. If it is not found, it is searched from the variable object of the execution context of the parent (lexical level parent) until it finds the variable object of the global context, that is, the global object. Thus a linked list of variable objects in multiple execution contexts is called a scoped chain. Since JS uses lexical scope, the scope of a function is already defined when it is defined. This is because the function has an internal property [[scope]], which holds all the parent objects when the function is created. We can think of [[scope]] as a hierarchy of all the parent objects ([[scope]] does not represent the full scope chain). Such as:

function foo(){
	function bar(){
    }
}
Copy the code

At creation time [[scope]] is:

foo.[[scope]] = [globalContext.VO]
bar.[[scope]] = [fooContext.AO,globalContext.VO]
Copy the code

Scope =[AO].concat([[scope]]) scope=[AO].concat([[scope]])

Summary of Execution Process

So let’s clean up the process of creating variable objects and executing scoped chains in the execution context. Example functions:

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();
Copy the code

The process steps are as follows:

  1. Executes global code, creating a global execution context that is pushed into the execution context stack
ECStack= [globalContext]
Copy the code
  1. Global context initialization
globalContext={
    VO: [global].Scope:[globalContext.VO],
    this:globalContext.VO 
}
Copy the code
  1. At the same time of initialization, the checkScope function is created, saving the scope chain on [[scope]] and pushing it onto the execution context stack
ECStack = [
    checkscopeContext,
    globalContext
]
Copy the code
  1. The checkScope function performs context initialization,
    • Copy the [[scope]] property to create the scope chain
    • Use Arguments to create live objects
    • Initialize live objects, add parameters, function declarations, and variable declarations
    • Push the live object into the top of the CheckScope chain.
    • At the same time the f function is created, saving the internal properties of the scope chain to the F function [[scope]]
checkscopeContext = {
  AO:{
      arguments:{
      	length:0
      },
      scope:undefinded,
      f: reference to function f(){}
  }
  Scope:[AO,[[scope]]],
  this:undefinded
}
Copy the code
  1. The checkScope function is executed, and the properties of the variable object change
checkscopeContext = {
  AO:{
      arguments:{
      	length:0
      },
      scope:"local scope",
      f: reference to function f(){}
  }
  Scope:[AO,[[scope]]],
  this:undefinded
}
Copy the code
  1. The checkScope function execution completes, the CheckScope function execution context pops from the execution context stack, and the F function execution context is initialized.
ECStack = [
    fContext,
    globalContext
]
fContext = {
  AO:{
      arguments:{
      	length:0
      },
  }
  Scope:[AO,checkscopeContext.AO,[[scope]]],
  this:undefinded
}
Copy the code
  1. Since scope maintains checkScopecontext. AO, the corresponding value of scope can still be found when f function is executed. When the f function completes execution, the f function execution context pops up from the execution context stack
ECStack = [
    globalContext
]
Copy the code

Reference Documents:

  • JavaScript deep lexical scope and dynamic scope
  • JavaScript deep execution context stack
  • JavaScript deep variable objects
  • JavaScript deep into the execution context