Frontend Development 13 min read

Master JavaScript Execution Contexts and Call Stack: A Deep Dive

This article explains how JavaScript execution contexts and the call stack work, covering their types, creation and execution phases, this binding, lexical and variable environments, and includes code examples to illustrate these core concepts for developers.

Yuewen Frontend Team
Yuewen Frontend Team
Yuewen Frontend Team
Master JavaScript Execution Contexts and Call Stack: A Deep Dive

If you're a JavaScript developer or aspiring to be one, you need to understand how JavaScript programs execute internally. Grasping execution contexts and the call stack also helps you understand related concepts such as hoisting, scope, and closures.

What is an Execution Context

In short, an execution context is the abstract environment in which JavaScript code is parsed and executed. All code runs inside an execution context.

Types of Execution Contexts

Global Execution Context: The default, most basic context. Code not inside any function runs here. It creates a global object (window in browsers) and binds this to it. Only one global context exists per program.

Function Execution Context: Created each time a function is called. Each function gets its own context, which is created only upon invocation. Multiple function contexts can exist.

Eval Function Execution Context: Code executed inside eval also gets its own context, but it is rarely used.

Execution Stack (Call Stack)

The execution stack, also called the call stack, is a LIFO structure that stores all execution contexts created during code execution.

When the engine first reads a script, it creates the global execution context and pushes it onto the stack. Each function call creates a new context that is pushed on top. The engine runs the context at the top; when it finishes, the context is popped, and control returns to the next context.

Example:

<code>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');</code>

When this code runs, the engine creates the global context, then pushes the first() context, then the second() context, and pops them as they complete, finally returning to the global context.

Execution stack diagram
Execution stack diagram

How Execution Contexts Are Created

Contexts are created in two phases: the creation phase and the execution phase.

Creation Phase

During the creation phase, three things happen:

Determine the value of this (This Binding).

Create the LexicalEnvironment component.

Create the VariableEnvironment component.

The abstract representation:

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

This Binding

In the global context, this points to the global object (window in browsers). In a function context, this depends on how the function is called: an object method call binds this to that object; otherwise it defaults to the global object or undefined in strict mode.

<code>let person = {
  name: 'peter',
  birthYear: 1994,
  calcAge: function() {
    console.log(2018 - this.birthYear);
  }
};

person.calcAge(); // 'this' is person

let calculateAge = person.calcAge;
calculateAge(); // 'this' is global (or undefined in strict mode)</code>

Lexical Environment

The ES6 specification defines a lexical environment as a structure that maps identifiers to their bindings. It consists of an environment record and a reference to an outer lexical environment (which may be null).

A lexical environment is a specification type that defines the association between identifiers and the variables/functions they refer to, based on the lexical nesting of ECMAScript code.

There are two kinds of lexical environments:

Global Environment: No outer environment (outer is null). It contains the global object and its properties; this points to the global object.

Function Environment: Stores variables declared inside a function in its environment record; its outer reference may be the global environment or another function’s environment.

Each function environment’s record also includes an arguments object that maps argument indices to values and stores the argument count.

<code>function foo(a, b) {
  var c = a + b;
}
foo(2, 3);
// arguments: {0: 2, 1: 3, length: 2}</code>

Variable Environment

The variable environment is also a lexical environment whose EnvironmentRecord holds bindings created by variable statements. In ES6, the LexicalEnvironment stores let and const bindings, while the VariableEnvironment stores var bindings.

Example illustrating both environments:

<code>let a = 20;
const b = 30;
var c;

function multiply(e, f) {
  var g = 20;
  return e * f * g;
}

c = multiply(20, 30);
</code>

Resulting execution contexts (simplified):

<code>GlobalExecutionContext = {
  ThisBinding: <Global Object>,
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      a: <uninitialized>,
      b: <uninitialized>,
      multiply: <func>
    },
    outer: null
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      c: undefined
    },
    outer: null
  }
}

FunctionExecutionContext (for multiply) = {
  ThisBinding: <Global Object>,
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      Arguments: {0:20,1:30,length:2}
    },
    outer: GlobalLexicalEnvironment
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      g: undefined
    },
    outer: GlobalLexicalEnvironment
  }
}
</code>

Execution Phase

In this phase, all variable assignments are performed and the code is actually executed. If a let or const variable is not found at its declared location, the engine assigns undefined .

Conclusion

Understanding execution contexts and the call stack helps you become a better JavaScript developer and makes it easier to grasp related concepts such as hoisting, scope, and closures.

JavaScriptExecution ContextCall Stackthis bindinghoistingLexical Environment
Yuewen Frontend Team
Written by

Yuewen Frontend Team

Click follow to learn the latest frontend insights in the cultural content industry. We welcome you to join us.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.