Skip to content

JavaScript Internals: The Execution Context

Published: at 09:21 AM

Table of contents

Open Table of contents

Introduction

In this article, I am going to delve into one of the internals and core features of JavaScript program execution know as the execution context. I will explain the types of execution context, what is contained in each execution context, and phases in the activation of execution contexts. Ta-da! Let’s begin 😀.

What is Execution Context?

Execution context in JavaScript is simply the space or environment in which a particular JavaScript scope’s code (global and function scope) is executed. When an execution context is created, it gets pushed onto the call stack. The call stack is a data structure that keeps track of execution contexts. There are essentially two types of execution context that gets created and executed when the engine runs a JavaScript code: global and function execution contexts. The first that gets created and pushed onto the call stack is the global execution context, followed by function execution contexts which also gets created and pushed onto the call stack when they are invoked. When an execution context completes its execution, it gets popped out of the call stack and control of execution is moved to the execution context below it in the call stack. The global execution context is the last to complete its execution and popped out of the call stack.

// Global scope
var globalVar = "I am in the global scope";

function func1() {
  var func1Var = "I am in func1";

  function func2() {
    var func2Var = "I am in func2";

    function func3() {
      var func3Var = "I am in func3";

      function func4() {
        var func4Var = "I am in func4";
        // func4 has access to variables in its own scope,
        // as well as those in the scopes of func3, func2, func1, and the global scope
        console.log(func4Var); // Accesses its own variable
        console.log(func3Var); // Accesses func3's variable
        console.log(func2Var); // Accesses func2's variable
        console.log(func1Var); // Accesses func1's variable
        console.log(globalVar); // Accesses global variable
      }

      func4(); // Invoke func4 to demonstrate access to outer environments
    }

    func3();
  }

  func2();
}

func1();

Code Example 1

Consider the code above, the equivalent execution contexts that will be created for the global scope and each of the function calls is represented in the image below.

JavaScript call stack and execution contexts Figure 1: Execution context for Code Example 1

Note: Execution context is only created for function and global scopes. It is not created for block scopes.

What’s Contained in Each Execution Context?

By now we all know that when a JavaScript program gets executed, a global execution context is created, and function execution contexts are created if the program has in it functions that are invoked. If execution context is the environment in which a global scope or a function scope is executed, then it should contain within that environment all the necessary artifacts to execute that scope’s code. Each execution context contains variable environment, this, and the outer environment.

It’s okay if you are confused at this point. Don’t panic, I will add more light with clear examples for better understanding.

Phases in Execution Context

There are essentially two phases in execution context, and they include creation phase and execution phase. Let’s explain each of these phases.

Creation Phase

The JavaScript engine performs various key actions during the creation phase of an execution context. It is in this phase that the JavaScript engine sets the variable environment, scope chain, and the this value. Variable and function declarations are hoisted, and the scope chain is established via the outer environment. Below are the key actions performed by the JavaScript engine.

Execution Phase

The next phase after the creation phase is the execution phase. In this phase, the JavaScript engine executes the code in that execution context (global or function) line by line. Below are key operations performed by the JavaScript engine during the execution phase.

Important Points to Note

Practical Examples

Let’s explore a few examples to further solidify our understanding on “execution context“.

Example 1

Consider the code below.

// Global scope
const objective = "Greeting Program";

function sayHello() {
  // Function scope
  const greet = "Hello World!";
  console.log(greet);

  sayHi();
}

function sayHi() {
  // Function Scope
  const greet = "Hi, Marie!";
  console.log(greet);
}

console.log(objective);

sayHello();

Code Example 2

For “Code Example 2“ which is the code above, below is the call stack and execution contexts for the global, sayHello function, and sayHi function.

The call stack and execution context for the above code Figure 2: Execution context for Code Example 2

Here are the steps of actions and the corresponding execution context activations for the code above:

  1. The Global Execution Context (GEC) is created and pushed onto the call stack as the script starts execution. The variable objective is stored in its environment variable, and the functions sayHello and sayHi are hoisted, stored in its environment variable, and available for execution.
  2. When console.log(objective); executes, objective is accessed from the global execution context’s variable environment.
  3. Upon calling sayHello(), a new execution context for sayHello is created and pushed onto the call stack. The variable greet is stored in sayHello environment variable, and is accessible only within the sayHello context. The call to sayHi() from within sayHello creates another execution context for sayHi.
  4. The execution context for syaHi is then pushed onto the call stack, creating its own variable environment for its greet variable. After sayHi completes execution, its execution context is popped off the call stack, returning control to the next execution context of sayHello.
  5. Once sayHello completes execution, its execution context is also popped off the call stack, returning control to the global execution context.

Quick Questions

Observe that in figure 2 of this article, the outer environment of the global execution context is null, and the outer environment of the sayHello() and sayH() execution contexts is global respectively.

Further Reading