What is execution context?


In short, an execution context is an abstraction of the environment in which JavaScript code is evaluated and executed. Whenever Javascript code is running, it is running in an execution context.

 Type of execution context

 There are three execution context types in JavaScript.


  • Global Execution Context – This is the default or base context, any code that is not inside a function is in the global context. It does two things: it creates a global window object (in the case of the browser) and it sets the value of this equal to this global object. There can only be one global execution context in a program.

  • Function Execution Context – Whenever a function is called, a new context is created for that function. Each function has its own execution context, but it is created when the function is called. There can be any number of function contexts. Whenever a new execution context is created, it performs a series of steps in a defined order (discussed later).

  • Eval Function Execution Context – The code executing inside the eval function will also have its own execution context, but since JavaScript developers don’t use eval very often, I won’t discuss it here.


The execution stack, or “call stack” as it is called in other programming languages, is a stack with a LIFO (last in, first out) data structure that is used to store all the execution context created when the code is run.


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


The engine executes those functions whose execution context is at the top of the stack. When the execution of that function finishes, the execution context is popped off the stack and the control flow reaches the next context in the current stack.

 Let’s go through the following code example to understand:

let a = 'Hello World!';

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

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

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

 The execution context stack for the above code.


When the above code loads in the browser, the JavaScript engine creates a global execution context and presses it into the current execution stack. When the 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 the second() function is called from within the first() function, the JavaScript engine creates a new execution context for the second() function and presses it into the top of the current execution stack. When the second() function finishes executing, its execution context is popped off the current stack and controls the flow to the next execution context, which is the execution context of the first() function.


When first() finishes executing, its execution context is popped off the stack and the control flow 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?


So far, we’ve seen how JavaScript manages execution contexts, now let’s see how the JavaScript engine creates them.


There are two phases to create an execution context: 1) the creation phase and 2) the execution phase.

The Creation Phase


Before JavaScript code is executed, the execution context goes through a creation phase. Three things happen during the creation phase:


  1. The decision on the value of this is what we know as the This binding.
  2.  Create lexical environment components.
  3.  Create variable environment components.

 So the execution context is conceptually represented as follows:

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

 This Binding:


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


In the context of function execution, the value of this depends on how the function was called. If it is called by a referenced object, then this is set to that object, otherwise the value of this is set to the global object or undefined (in strict mode). Example:

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

foo.baz();   

let bar = foo.baz;

bar();      

  lexical environment

  The official ES6 documentation defines lexical environments as


A lexical environment is a canonical type that defines associations between identifiers and concrete variables and functions based on the lexically nested structure of ECMAScript code. A lexical environment consists of an environment recorder and a possible null value that references an external lexical environment.


In simple terms a lexical environment is a structure that holds an identifier-variable mapping. (Identifier here means the name of the variable/function, and variable is a reference to the actual object [containing the function type object] or raw data.)


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

  1.  The environment logger is the actual location where variables and function declarations are stored.

  2. A reference to an external environment means that it has access to its parent lexical environment (scope).

 There are two types of lexical environments:


  • A global environment (in a global execution context) is a lexical environment with no external environment reference. The global environment has an external environment reference of null. It has a built-in Object/Array/etc., a prototype function within the environment recorder (associated with a global object, such as a window object), and any user-defined global variables, and the value of this points to the global object.

  • In a function environment, user-defined variables inside the function are stored in the environment logger. And the referenced external environment may be the global environment, or any external function that contains this internal function.

 There are also two types of environmental loggers (as above!) :

  1.  Declarative environment recorders store variables, functions, and parameters.

  2. The object context recorder is used to define the relationships of variables and functions that appear in the global context.

 In a nutshell.

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


Note – For function environments, the declarative environment recorder also contains a arguments object passed to the function (this object stores the mapping of indexes to arguments) and the length of the arguments passed to the function.

 In the abstract, 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 recorders hold the bindings created by variable declaration statements in the execution context.


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


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

 Let’s look at some sample code to understand the concepts above:

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 a call to the function multiply is encountered.


You may have noticed that the variables defined by let and const do not have any values associated with them, but the variable defined by var is set to undefined .


This is because during the creation phase, the engine examines the code to find variable and function declarations, and although the function declarations are fully stored in the environment, the variables are initially set to undefined (in the case of var ), or uninitialized (in the case of let and const ).


That’s why you can access variables defined by var before declaration (although undefined ), but accessing variables at let and const before declaration will get a reference error.

 This is what we call variable declaration lifting.

 Implementation phase


This is the easiest part of the whole article. At this stage, the assignment of all these variables is completed and finally the code is executed.


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 will be assigned to undefined .

By hbb

Leave a Reply

Your email address will not be published. Required fields are marked *