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
thisdepends on how function is called- Closures capture the scope chain