Scope and Hoisting
Deep dive into JavaScript scope chain and variable hoisting
Challenge
Section titled “Challenge”Predict the output of this code snippet. Why does x have different values in different scopes?
console.log(x); // ?var x = 5;if (true) { var x = 10; let y = 15; const z = 20;}console.log(x); // ?console.log(y); // ?Explanation
Section titled “Explanation”JavaScript has function scope for var and block scope for let/const. Hoisting moves declarations to the top of their scope, but not initializations.
Solution
Section titled “Solution”// var is hoisted to function scopeconsole.log(x); // undefined (hoisted but not initialized)var x = 5;
if (true) { var x = 10; // Overwrites the function-scoped x let y = 15; // Block-scoped const z = 20; // Block-scoped}
console.log(x); // 10 (modified inside if block)console.log(y); // ReferenceError: y is not definedconsole.log(z); // ReferenceError: z is not definedHoisting Details
Section titled “Hoisting Details”// This code:console.log(typeof func); // "function"console.log(typeof func2); // "undefined"
function func() {} // Function declaration is fully hoisted
var func2 = function() {}; // Only var declaration is hoisted
// Is interpreted as:var func2; // Hoisted declarationfunction func() {} // Fully hoisted
console.log(typeof func); // "function"console.log(typeof func2); // "undefined"
func2 = function() {}; // Assignment happens hereScope Chain and Closures
Section titled “Scope Chain and Closures”const global = 'global';
function outer() { const outerVar = 'outer';
function inner() { const innerVar = 'inner'; console.log(innerVar); // 'inner' - local console.log(outerVar); // 'outer' - outer scope console.log(global); // 'global' - global scope }
return inner;}
const fn = outer();fn(); // Prints all three valuesBest Practices
Section titled “Best Practices”// ❌ Avoid: Using varvar x = 5;if (true) { var x = 10; // Overwrites outer x}
// ✅ Good: Use let/const for block scopelet x = 5;if (true) { let x = 10; // Creates new block-scoped variable}console.log(x); // 5 - outer x unchanged
// ✅ Best: Prefer const, use let when reassignment neededconst API_URL = 'https://api.example.com';let count = 0;Temporal Dead Zone (TDZ)
Section titled “Temporal Dead Zone (TDZ)”function example() { console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 5; // let/const create a TDZ before the line they're declared}
// This is different from var:function example2() { console.log(x); // undefined (hoisted) var x = 5;}