Variable Scope and Hoisting
Understanding variable scope and hoisting behavior
Challenge
Section titled “Challenge”Understand why code behaves unexpectedly with var, let, and const. Predict execution order and scope.
Explanation
Section titled “Explanation”JavaScript has function scope (var), block scope (let, const), and hoisting which affects when variables are accessible.
Variable Scope Types
Section titled “Variable Scope Types”// Global scopeconst globalVar = 'global';
function testScope() { // Function scope const functionVar = 'function';
if (true) { // Block scope (let, const only) const blockVar = 'block'; }
// blockVar not accessible here (if using let/const)}
// globalVar accessible everywhereVar vs Let vs Const
Section titled “Var vs Let vs Const”// var: Function scoped, redeclarablefunction testVar() { if (true) { var x = 1; } console.log(x); // 1 (accessible outside if block)}
// let: Block scoped, not redeclarablefunction testLet() { if (true) { let y = 1; } // console.log(y); // Error: y is not defined}
// const: Block scoped, immutable referenceconst obj = { name: 'John' };// obj = {}; // Error: Assignment to constant variableobj.name = 'Jane'; // OK: Mutating object is allowed
const arr = [1, 2, 3];arr.push(4); // OK// arr = []; // ErrorHoisting
Section titled “Hoisting”// var hoistingconsole.log(x); // undefined (not ReferenceError)var x = 5;console.log(x); // 5
// Equivalent to:var x; // Declaration hoistedconsole.log(x); // undefinedx = 5;console.log(x); // 5
// Function hoistingconsole.log(foo()); // 'Hello' (entire function hoisted)function foo() { return 'Hello';}
// Function expression not hoisted// console.log(bar()); // TypeError: bar is not a functionconst bar = () => 'Hello';console.log(bar()); // 'Hello'Temporal Dead Zone (TDZ)
Section titled “Temporal Dead Zone (TDZ)”// TDZ: let/const not accessible before declaration// console.log(y); // ReferenceError: Cannot access 'y' before initializationlet y = 5;console.log(y); // 5
// Even if outer scope has same variableconst z = 'outer';{ // console.log(z); // ReferenceError (in TDZ) const z = 'inner'; console.log(z); // 'inner'}Shadowing
Section titled “Shadowing”const x = 'outer';
function test() { // Shadowing: inner variable hides outer const x = 'inner'; console.log(x); // 'inner'}
test();console.log(x); // 'outer'Closure Scope
Section titled “Closure Scope”function createCounter() { let count = 0;
return { increment() { count++; return count; }, decrement() { count--; return count; } };}
const counter = createCounter();console.log(counter.increment()); // 1console.log(counter.increment()); // 2console.log(counter.decrement()); // 1
// count is not directly accessible but persists// This is a closure retaining access to countModule Scope
Section titled “Module Scope”// JavaScript modules have file scope// Variables declared at top are private to moduleconst privateVar = 'private';
export const publicVar = 'public';
// Only publicVar is accessible when imported// privateVar remains hiddenBest Practices
Section titled “Best Practices”// 1. Prefer const by defaultconst value = 5; // Immutable reference
// 2. Use let for mutable valueslet counter = 0;counter++;
// 3. Avoid var (legacy)// var legacy = 'avoid';
// 4. Minimize scopeif (condition) { const tempVar = compute(); // Scoped to if block console.log(tempVar);}// tempVar not accessible here
// 5. Avoid hoisting confusionfunction process() { // Declare at top to make intent clear const result = calculate(); return result;}