Skip to main content

JavaScript Execution Context and Scope Chain

Answer

The execution context is the environment in which JavaScript code is executed. It contains information about variable scope, this binding, and the outer environment reference (scope chain).

Execution Context Creation

Call Stack

function outer() {
console.log("Outer start");
inner();
console.log("Outer end");
}

function inner() {
console.log("Inner");
}

outer();
// Outer start
// Inner
// Outer end

Execution Context Phases

// 1. CREATION PHASE
// - Creates variable object (hoisting)
// - Creates scope chain
// - Determines 'this' value

// 2. EXECUTION PHASE
// - Assigns values to variables
// - Executes code line by line

function example(a) {
var b = 10;

function inner() {
return a + b;
}

return inner();
}

// Creation phase creates:
// {
// arguments: { 0: value, length: 1 },
// a: value,
// b: undefined, // hoisted but not assigned
// inner: function() // fully hoisted
// }

// Execution phase assigns:
// b = 10
// Calls inner()

Scope Chain

var global = "global";

function outer() {
var outerVar = "outer";

function inner() {
var innerVar = "inner";

console.log(innerVar); // Found in current scope
console.log(outerVar); // Found in outer scope
console.log(global); // Found in global scope
}

inner();
}

outer();

Lexical Scope

// Scope is determined by WHERE functions are WRITTEN
// Not where they are CALLED

function outer() {
var x = 10;

function inner() {
console.log(x); // 10 - lexical scope from outer
}

return inner;
}

var x = 20;
var innerFn = outer();
innerFn(); // 10, not 20 (lexical scoping, not dynamic)

Variable Environment vs Lexical Environment

// Variable Environment: var, function declarations
// Lexical Environment: let, const (block-scoped)

function example() {
// Variable Environment
var a = 1;

if (true) {
// New Lexical Environment for this block
let b = 2;
const c = 3;
var d = 4; // Goes to Variable Environment

console.log(a, b, c, d); // 1, 2, 3, 4 - all accessible
}

console.log(a, d); // 1, 4
console.log(b); // ReferenceError: b is not defined
}

this Binding

// 'this' is determined by HOW a function is CALLED

const obj = {
name: "Object",

regularFunction: function () {
console.log(this.name); // "Object" - called on obj
},

arrowFunction: () => {
console.log(this.name); // undefined - inherits from outer scope
},
};

obj.regularFunction(); // "Object"
obj.arrowFunction(); // undefined

// Different call patterns
const fn = obj.regularFunction;
fn(); // undefined (or error in strict mode)
fn.call({ name: "Call" }); // "Call"
fn.apply({ name: "Apply" }); // "Apply"
fn.bind({ name: "Bind" })(); // "Bind"

Closures and Scope Chain

function createCounter() {
let count = 0; // Captured in closure

return {
increment() {
count++; // Accesses outer scope
return count;
},
decrement() {
count--; // Same outer scope reference
return count;
},
getCount() {
return count;
},
};
}

const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.getCount(); // 2

// Each call to createCounter creates NEW scope
const counter2 = createCounter();
counter2.getCount(); // 0 (independent)

Module Pattern Using Scope

const Module = (function () {
// Private scope
let privateVar = 0;

function privateFunction() {
return privateVar;
}

// Public API (closure over private scope)
return {
increment() {
privateVar++;
},
get value() {
return privateFunction();
},
};
})();

Module.increment();
console.log(Module.value); // 1
console.log(Module.privateVar); // undefined (private)

Common Interview Question

// What does this print?
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}
// Answer: 3, 3, 3 (var is function-scoped, shared)

// Fix 1: Use let (block-scoped)
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}
// Answer: 0, 1, 2

// Fix 2: Create new scope with IIFE
for (var i = 0; i < 3; i++) {
((j) => {
setTimeout(() => {
console.log(j);
}, 1000);
})(i);
}
// Answer: 0, 1, 2

Key Points

  • Execution context = scope + this + outer reference
  • Call stack manages execution contexts
  • Creation phase: hoisting, scope chain setup
  • Execution phase: assignments, code execution
  • Scope chain follows lexical (written) structure
  • this depends on how function is called
  • Closures capture the scope chain