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