JavaScript Execution Context
Tags: executioncontext, javascript, es6
Published: 2021-04-25 03:32:45

<blockquote>It is important to view knowledge as sort of a semantic tree — make sure you understand the fundamental principles, ie the trunk and big branches, before you get into the leaves/details or there is nothing for them to hang on to.
 — <cite>Elon Musk on <a href="https://www.reddit.com/r/IAmA/comments/2rgsan/i_am_elon_musk_ceocto_of_a_rocket_company_ama/cnfre0a/" target="_blank">reddit</a></cite></blockquote>

In this article we will take a look at the most important concept or the fundamental principle of the JavaScript language, that is the Execution Context. The reason for that is because if you have a good understanding of the JavaScript Execution Context, you'll have a much easier time understanding some more advanced topics like hoisting, scopes, scope chains, and closures.

Now with that in mind let's dive right into it.

## The Execution Context

Execution context is an abstract concept of an environment where the JavaScript code is executed. You can think of the execution context as a large container that can be used to hold or load things in and process. Within the large container are other smaller containers. Now the question you might ask is, who manages those containers?

## The JavaScript Engine

The thing that is responsible for managing the JavaScript execution context is the JavaScript Engine. Each browser has its version of the JavaScript engine. Chrome uses [V8 Engine](https://v8.dev/), Firefox uses [SpiderMonkey](https://spidermonkey.dev/), and Safari uses [JavaScriptCore](https://trac.webkit.org/wiki/JavaScriptCore). The first thing the JS engine does is it downloads the JS source code. Once the code is received, it runs into a parser and creates an [Abstract Syntax Tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree) — a tree-like representation of JS code. After that, it then enters into a global execution context by default. And each invocation or calls to a function on the JS code from this point on will result in the creation of a new local execution context. Every execution context has 2 phases which we will take a look at later in this article. The JS engine also manages the memory allocation (heap memory), garbage collection (Orinoco), code conversion to bytecode (V8 Ignition), and optimization to machine code (V8 TurboFan). The JS engine is another topic of its own so we won't cover that in this article. 

## Types of Execution Context

- Global Execution Context (The large container) 
- Local/Function Execution Context (The small container)
- Eval Function Execution Context (The small container)

#### Global Execution Context

This is the default execution context that the JS engine enters after it loads and parses the JS code. Once the JS engine is inside the global execution context it will create two properties in the global memory by default, the `window` object (in the case of browsers) and the `this` object. 

The `window` object points to or is wired to the `global` object — an object that is always created before the JS engine enters the global execution context which has properties and methods such as localStorage, innerWidth, event handlers, etc.

The `this` object (in the global execution context) is an object that points to or is wired to the `window` object.

So, what will happen if there are variables and functions declared in the JS code? What it will do is it will scan through all the code and look for every variable and function declarations (variables and functions that are not nested to any other function) and store it in the global memory together with `window` and `this` objects.

|Global Memory||
|:-----|:-----|
|window|< ref. to Global Obj. >|
|this|< ref. to window Obj. >|
|var_identifier|var_value|
|fn_identifier|< fn Obj. >|

NOTE: The function object is the snapshot of the whole function stored in the memory.

#### Local/Function Execution Context

Every time a function is called or invoked, a brand new execution context is created for that function. Every function has its local execution context which is created once the JS engine encounters a function call. Inside the local execution context, the JS engine will create an `arguments` object and a `this` object by default.

The `arguments` object contains a key:value pair of parameters expected inside a function. It also contains a default property called `length`, that counts the numbers of parameters for that function. The `arguments` object defaults to `{ length: 0 }` when the function's argument is empty. 

The `this` object inside the function execution context varies depending on how the function is called. If it is called by an object reference, then the value of `this` is set to that object. Otherwise, the value of `this` is set to the `window` object or will be of value "undefined" (in strict mode).

|Local Memory||
|:-----|:-----|
|arguments|{ length: 0 }|
|this|< ref. to window Obj. >|
|var_identifier|var_value|
|fn_identifier|< fn Obj. >|

#### Eval Function Execution Context

The `eval` function is a dangerous function. Whenever the JS engine encounters an `eval()` function, an execution context is created and is pushed into the call stack. It accepts a string as a parameter and evaluates it. So if you accidentally passed a malicious code in its argument, or a malicious party exploits this part of your code, then your website could potentially be severely damaged. It is not recommended to use this function as there are better alternatives to it. You can learn more about `eval` function [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#never_use_eval!).

## How does the JS engine know the currently running Execution Context?

JavaScript is a single-threaded language which means that it can only run a single task at a time. An example of a task is declaring a variable, assigning a value to a variable, or calling a function. We already know that a function call sets up a local/function execution context. Under the hood when the JS engine encounters a function call its task is to push that execution context into memory and pops it off when the code inside of it is done. That memory is called the **Execution Stack** also known as the **_"Call Stack"_**. It is an array of execution contexts that uses a LIFO (Last In First Out) data structure. It is used by the JS engine to keep track of execution context by storing each call into the memory. The global execution context is present by default in the call stack and it is at the bottom of the stack. While executing the global execution context code, if the JS engine finds a function call, it creates a local execution context for that function and pushes it to the top of the call stack. The JS engine then executes the function whose execution context is at the top of the call stack. Once all the code of the function is executed, JS engine takes out that function execution context and start’s executing the function which is below it.

Let us try to understand that with an example:

```js
console.log("Initially I am inside the global execution context.");

let message = "Heyyow!";

function first() {
  console.log("I am inside the first function execution context");
  second();
  console.log("I am again inside the first function execution context");
}

function second() {
  console.log("I am inside the second function execution context");
}

first();
console.log("I am back at the global execution context.");
```

![Call stack process](https://luigicruz.dev/static/images/2021/execution-context/call-stack.gif)

View a PDF copy of the Call stack process [here](https://luigicruz.dev/static/pdf/call-stack.pdf).

Once the JS engine is done loading and parsing the JS code, it will set up a `global()` execution context and pushes it to the call stack. When it sees the function call `first()`, it will set up a new function execution context for that function and pushes it to the top of the call stack. When the JS engine encounters the function call `second()` inside the first execution context, it will create another execution context for that function and pushes it to the top of the call stack. Once the **_second()_** function finishes, its execution context is popped off from the call stack, and the control is transferred to its parent execution context **_first()_**. When the first execution context is finished, its execution context is removed from the call stack, and the control is transferred to its parent execution context **_global()_**. Once all the code is executed, the JS engine removes the global execution context from the call stack and exits.

## What exactly happens inside an Execution Context?

Now that we already know that the JS engine uses the call stack to keep track of the currently running execution context, let us now understand what exactly is happening inside an execution context.

The execution context has two phases: 1) the **Creation Phase** and 2) the **Execution Phase**.

## Creation Phase

This is the stage that the JS engine enters after the JS code is loaded and has been parsed. Every execution context has a Creation Phase. The creation of an execution context is part of the creation phase. Two [state components](https://tc39.es/ecma262/#table-additional-state-components-for-ecmascript-code-execution-contexts) are created during the creation phase:

- **Lexical Environment**, and
- **Variable Environment**

Conceptually, the execution context is represented as follows:

```js
GlobalExecutionContext = {
  LexicalEnvironment : { },
  VariableEnvironment : { },
}
```

A **Lexical Environment** component is a structure that defines the association of identifiers to the values of variables and functions based upon the lexical nesting structure of JS code. This association of identifier to the values of variables and functions is called `binding`. 

A **Variable Environment** component is also a **Lexical Environment** that defines the association of identifiers to the values of variables but not functions.

The difference between the two is in the variable that the identifier is bounded. The _Lexical Environment_ is used to store bindings of an identifier to the values of the variables (`let` and `const`) and functions, while the _Variable Environment_ is used to store bindings of an identifier to the values of the variable (`var`) only.

I'm confused. What is exactly inside the Lexical Environment?

### Environment Record

Each _Lexical Environment_ has an [Environment Record](https://tc39.es/ecma262/#sec-environment-records). The _Environment Record_ records the identifier bindings that are created within the scope of the lexical environment. Each time a JS code is evaluated (var/func declarations or assignments), a new Environment Record is created to record the identifier bindings that are created by that code. This environment in JavaScript is called the `scope`.

Every **Environment Record** has an **[[OuterEnv]]** field, which is either _null_ or a reference to an outer Environment Record. The reason why a child function has access to its parent's scope is because of the outer environment object. For example, when the JS engine sees a variable inside a function, it will try to find the variable's value from the current function's environment record (local memory). If it could not find the variable inside of it, it will look into the outer scope (its parent environment record) up to the global scope until it finds that variable. This lookup process is called the `scope chain`. We'll try to take a look at the scope chain and dig deeper in future articles.

There are three type subclasses inside of the Environment Record:

- [Declarative Environment Record](https://tc39.es/ecma262/#sec-declarative-environment-records) — As its name suggests stores variables, classes, modules, and/or function declarations. A declarative Environment Record binds the set of identifiers defined by the declarations contained within its scope.
- [Object Environment Record](https://tc39.es/ecma262/#sec-object-environment-records) — This environment record in the global execution context contains the bindings for all built-in globals. This is the [window object](https://developer.mozilla.org/en-US/docs/Web/API/Window) that references the [global object](https://tc39.es/ecma262/#sec-global-object). Variables and functions that are of global scope are added to the global execution context's object environment record that is why you can access global variables such as _`window.localStorage`_ and _`window.var_name`_. In the local/function execution context the object environment record is composed of the `arguments` object and the `this` object.
- [Global Environment Record](https://tc39.es/ecma262/#sec-global-environment-records) — A global Environment Record is logically a single record but it is specified as a composite encapsulating an **_object Environment Record_** and a **_declarative Environment Record_**. It does not have an outer environment; it's **[[OuterEnv]]** is null. It may be prepopulated with identifier bindings and it includes an associated global object whose properties provide some of the global environment's identifier bindings. As JS code is executed, additional properties may be added to the global object and the initial properties may be modified.

Now, let us conceptually visualize the execution context inside the **Creation Phase**. Take a look at this JS code:

```js
var name = "Luigi";
let input = "Hello, World!";

function broadcast(message) {
  return `${name} says ${message}`;
}

console.log(broadcast(input));
```

Note that when I say **_conceptually_**, it means that the pseudocode below is not the concrete representation of the environment the JS engine creates, but only to learn the concept by trying to visualize it. 

```js
GlobalExecutionContext = {
  LexicalEnvironment : {
    EnvironmentRecord : {
      DeclarativeEnvironmentRecord : {
        input: <uninitialized>,
        broadcast: < function broadcast(message) {
          return `${name} says ${message}`;
        } >,
      }, // Bindings of identifier to variables (`let` and `const`) and identifier to function objects
      ObjectEnvironmentRecord : {
        window: < ref. to Global obj. >,
        this: < ref. to window obj. >,
      },
      OuterEnv : < null >, // ref. to parent env. record (null in here since global has no parent execution context)
    }, 
  },
  VariableEnvironment : {
    EnvironmentRecord : {
      DeclarativeEnvironmentRecord : {
        name: undefined,
      }, // Bindings of identifier to variables (`var`)
    },
  },
}
```

Let's try to go through each step of what's happening inside the **Creation Phase** using the code snippet above:

1. The JS engine enters the Creation Phase.
2. Creates a global execution context and pushes it into the call stack.
3. Create bindings for the **window object** to the **Global object**.
4. Create bindings for the **this object** to the **window object**. Note that **this object** binding will vary depending on how the function is called and on _strict mode_.
5. Creates an identifier **_name_** in the _global memory_ and initializes it with a value of `undefined`. This process is called [hoisting](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting).
6. Creates an identifier **_input_** in the _global memory_ without initializing it or no initial value set.
7. Creates an identifier `broadcast` in the _global memory_ and store the whole function definition of the broadcast function in it. This function is also hoisted.

Next, we'll talk about how does the JS code gets executed.

## Execution Phase

This is the stage that the JS engine enters after all variables and functions are declared and necessary objects have bounded. Every execution context has an Execution Phase. Few things are happening inside this phase, the variable binding initializations, variable assignments, mutability and immutability checking, variable binding deletions, function call execution, etc.

Let's try to understand that by continuing the steps we wrote from the Creation Phase:

1. The JS engine enters the Creation Phase.
2. Creates a global execution context and pushes it into the call stack.
3. Create bindings for the **window object** to the **Global object**.
4. Create bindings for the **this object** to the **window object**. Note that **this object** binding will vary depending on how the function is called and on _strict mode_.
5. Creates an identifier **_name_** in the _global memory_ and initializes it with a value of `undefined`. This process is called [hoisting](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting).
6. Creates an identifier **_input_** in the _global memory_ without initializing it or no initial value set.
7. Creates an identifier `broadcast` in the _global memory_ and store the whole function definition of the broadcast function in it. This function is also hoisted.
8. The JS engine enters the Execution Phase.
9. Take the value of variable **_name_** and bind that value to the identifier inside the memory.
10. Take the value of variable **_input_** and bind that value to the identifier inside the memory.
11. Encounters a console log method, immediately evaluate the arguments inside it.
12. Sees a function call named `broadcast`, immediately creates a new local execution context for that function, and pushes it into the top of the call stack.
13. Enters the Creation Phase of the broadcast function execution context.
14. Creates **arguments object**, in the function's _local memory_ with an initial value of `{ length: 0 }`.
15. Add the passed parameter **_message_** to the first index of the arguments object.
16. Creates an identifier **_message_** in the function's _local memory_ and store the value that is passed to the function call's argument.
17. Goes inside the function block and evaluates the return statement.
18. Sees a variable **_name_**, it then performs a lookup of that variable inside the function's _local memory_.
19. It couldn't find the identifier **_name_** in the _local memory_ so it then continues to look for it from its parent scope (_global memory_). 
20. It finds the identifier **_name_** in the _global memory_ so it takes that value and swap it to the variable reference.
21. Sees a variable **_message_**, it then performs a lookup of that variable inside the function's _local memory_.
22. It finds the identifier **_message_** in the _local memory_ so it takes that value and swaps it to the variable reference.
23. Returns the evaluated result of the broadcast function execution context and is popped off from the call stack.
24. Pass the control to its calling context (the global execution context) with the returned result.
25. Displays `Luigi says Hello, World!` in the console.
26. Global execution context is popped off from the call stack and then the JS engine exits.

Whew, that's a lot! There's a lot more that is happening inside the Execution Phase like object mutability and immutability checking, etc... but I tried to simplify the steps so that I won't complicate the main idea in this article. But if you wanted to dig more though, you can read through at those from the [JavaScript Spec](https://tc39.es/ecma262/#sec-executable-code-and-execution-contexts).

So with all of that here's an updated pseudocode for all the execution context:

```js
GlobalExecutionContext = {
  LexicalEnvironment : {
    EnvironmentRecord : {
      DeclarativeEnvironmentRecord: {
        input: "Hello, World!",
        broadcast: {
          LocalExecutionContext : {
            LexicalEnvironment : {
              EnvironmentRecord: {
                DeclarativeEnvironmentRecord: {
                  message: "Hello, World!",
                },
                ObjectEnvironmentRecord: {
                  arguments: { 0: message, length: 1 }
                  this: < ref. to window obj. >
                },
              },
              OuterEnv: < ref. to LexicalEnvironment of the GlobalExecutionContext >,
            },
          },
        },
      },
      ObjectEnvironmentRecord: {
        window: < ref. to Global obj. >,
        this: < ref. to window Obj. >,
      },
      OuterEnv: < null >,
    },
  },
  VariableEnvironment : {
    EnvironmentRecord : {
      DeclarativeEnvironmentRecord: {
        name: "Luigi"
      },
    },
  },
}
```

To further explain the whole concept, I've created a high-level representation of the step-by-step process of the Execution Context using a GIF below. 

![Step by step execution process of JavaScript source code](https://luigicruz.dev/static/images/2021/execution-context/execution-context.gif)

View a PDF copy of the Execution context process [here](https://luigicruz.dev/static/pdf/execution-context.pdf).

There are few interesting topics that we didn't try to dive into deeper in this article. These are the `this object`, `scope chain`, `hoisting`, `garbage collection`, etc. We'll try to discuss those in future articles. 

I know that's a lot of things to absorb. You'll probably have to revisit this post multiple times to understand. While you don't need to learn all these concepts to be a good JavaScript developer, having a decent understanding of the above concepts will help you clear out those fogs at the more advanced topics. As Elon Musk said, focus on understanding the fundamental principles of things first, and later on you'll be surprised by the topic that you thought doesn't make sense — actually, isn't so hard at all.

Anyways, that's all I could share. I'll see you in my next post!