advertisement

Call Stack & Execution Context

Understand how JavaScript manages function execution, memory allocation, and the fundamental concepts that power the language's runtime.

Execution Context

An execution context is an abstract concept that holds information about the environment in which the current code is being executed. Think of it as a "container" that stores variables, functions, and the scope chain.

Types of Execution Context

Global Execution Context

Created when the script first runs. There's only one global context per program.

Function Execution Context

Created whenever a function is called. Each function has its own execution context.

Eval Execution Context

Created when code runs inside an eval() function (rarely used).

The Call Stack

The call stack is a LIFO (Last In, First Out) data structure that keeps track of execution contexts. When a function is called, its execution context is pushed onto the stack. When it returns, it's popped off.

Call Stack Visualization

main()
greet()
sayHello()
�?Top of Stack (currently executing) �?Called by greet() �?Global context
Example Code
function sayHello() {
    console.log("Hello!");
}

function greet() {
    sayHello();
    console.log("Greetings complete");
}

greet();
// Call Stack sequence:
// 1. main() - global context
// 2. greet() pushed
// 3. sayHello() pushed
// 4. sayHello() popped (after console.log)
// 5. greet() popped
// 6. main() continues

Creation Phase

When an execution context is created, the JavaScript engine goes through the creation phase before executing any code:

1

Create Variable Object (VO)

Scans for variable declarations (var), function declarations, and function arguments.

2

Create Scope Chain

Determines what variables the context has access to, including parent scopes.

3

Determine "this" Binding

Sets the value of the "this" keyword based on how the function was called.

Creation Phase Example
function example(x) {
    var a = 10;
    function inner() {
        return a + x;
    }
    var b = inner();
}

// During creation phase:
// VO = {
//     x: undefined,    // argument
//     a: undefined,    // var declaration
//     inner: fn,       // function declaration
//     b: undefined     // var declaration
// }

Execution Phase

After the creation phase, the engine executes the code line by line, assigning values to variables and executing function calls.

Execution Phase
function example(x) {  // x = 5 assigned
    var a = 10;         // a = 10 assigned
    function inner() {
        return a + x;
    }
    var b = inner();    // inner() called, b = 15
}

example(5);

// After execution:
// VO = {
//     x: 5,
//     a: 10,
//     inner: fn,
//     b: 15
// }

Hoisting

Hoisting is JavaScript's behavior of moving declarations to the top of their scope during the creation phase. This is why you can use functions before they're declared.

Important Distinction

  • Function declarations are fully hoisted (both declaration and definition)
  • var variables are hoisted but initialized as undefined
  • let/const are hoisted but remain in the "Temporal Dead Zone"
Hoisting Examples
// Function hoisting
sayHi();  // Works! Output: "Hi!"
function sayHi() {
    console.log("Hi!");
}

// var hoisting
console.log(x);  // undefined (not ReferenceError)
var x = 5;

// let/const - Temporal Dead Zone
console.log(y);  // ReferenceError!
let y = 10;

Stack Overflow

A stack overflow occurs when the call stack exceeds its maximum size, typically due to infinite recursion or extremely deep function calls.

Stack Overflow Example
// Infinite recursion - causes stack overflow
function forever() {
    forever();  // Calls itself infinitely
}

forever();  // Error: Maximum call stack size exceeded

// Solution: Add a base case
function countdown(n) {
    if (n <= 0) return;  // Base case stops recursion
    console.log(n);
    countdown(n - 1);
}

countdown(5);  // Works: 5, 4, 3, 2, 1

Key Takeaways

  • Every function call creates a new execution context
  • The call stack manages the order of execution
  • Understanding hoisting helps avoid common bugs
  • Always include base cases in recursive functions
  • JavaScript is single-threaded, one call stack only