Back

Understanding Execution Context in JavaScript

Published - November 19, 2024

Execution Context is a fundamental concept in JavaScript that defines the environment in which JavaScript code is executed. It contains information about variable scope, this binding, and much more.

Types of Execution Context

1. Global Execution Context (GEC)

// This is in Global Execution Context
let name = "John";
console.log(this); // Window object (in browser)

2. Function Execution Context (FEC)

function greet() {
    // This is Function Execution Context
    let message = "Hello";
    console.log(message);
}

3. Eval Execution Context

Created when code is executed inside an eval function (rarely used in modern JavaScript).

Phases of Execution Context

1. Creation Phase (Memory Creation)

During this phase:

console.log(name); // undefined (not error)
var name = "John";
 
sayHello(); // Works due to hoisting
function sayHello() {
    console.log("Hello!");
}

2. Execution Phase

During this phase:

var name = "John"; // Value assigned
console.log(name); // "John"

Execution Context Stack (Call Stack)

How it Works

function first() {
    console.log("First");
    second();
}
 
function second() {
    console.log("Second");
    third();
}
 
function third() {
    console.log("Third");
}
 
first();

Stack Operation:

  1. Global Execution Context
  2. First() pushed
  3. Second() pushed
  4. Third() pushed
  5. Third() popped
  6. Second() popped
  7. First() popped

Scope Chain

Example of Scope Chain

let globalVar = "I'm global";
 
function outer() {
    let outerVar = "I'm from outer";
    
    function inner() {
        let innerVar = "I'm from inner";
        console.log(innerVar); // Local scope
        console.log(outerVar); // From outer scope
        console.log(globalVar); // From global scope
    }
    
    inner();
}
 
outer();

Variable Environment

Let vs Var vs Const

{
    var x = 1; // Function scoped
    let y = 2; // Block scoped
    const z = 3; // Block scoped
}
 
console.log(x); // 1
console.log(y); // ReferenceError
console.log(z); // ReferenceError

Lexical Environment

function outer() {
    let count = 0;
    
    return function inner() {
        count++; // Has access to outer function's variables
        return count;
    }
}
 
const increment = outer();
console.log(increment()); // 1
console.log(increment()); // 2

This Binding

Different Contexts

// Global Context
console.log(this); // Window (browser)
 
// Object Method
const obj = {
    name: "Object",
    sayName() {
        console.log(this.name);
    }
};
 
// Constructor Function
function Person(name) {
    this.name = name;
}
 
// Event Handler
button.addEventListener('click', function() {
    console.log(this); // button element
});

Practical Examples

1. Closure with Execution Context

function createCounter() {
    let count = 0;  // Part of createCounter's execution context
    
    return {
        increment() {
            return ++count;
        },
        decrement() {
            return --count;
        }
    };
}
 
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1

2. Block Scoping Example

function example() {
    let x = 10;
    
    if(true) {
        let y = 20; // Block scoped
        var z = 30; // Function scoped
        console.log(x); // 10 - accessible
    }
    
    console.log(z); // 30 - accessible
    console.log(y); // ReferenceError - not accessible
}

Best Practices

  1. Always declare variables with appropriate scope
// Bad
function bad() {
    x = 10; // Global variable!
}
 
// Good
function good() {
    let x = 10; // Local variable
}
  1. Use block scope when possible
// Prefer
for(let i = 0; i < 5; i++) {
    // i is block scoped
}
 
// Instead of
for(var i = 0; i < 5; i++) {
    // i is function scoped
}
  1. Be mindful of this context
const obj = {
    name: "Object",
    greet: () => {
        // Arrow functions don't bind their own this
        console.log(this.name); // undefined
    },
    greetProperly() {
        console.log(this.name); // "Object"
    }
};

Understanding execution context is crucial for: