Hoisting
Hoisting is JavaScript's behavior of moving variable and function declarations to the top of their scope during compilation.
Variable Hoisting
var Hoisting
javascript// What you write: console.log(myVar); // undefined (not an error!) var myVar = 5; console.log(myVar); // 5 // How JavaScript interprets it: var myVar; // Declaration hoisted to top console.log(myVar); // undefined myVar = 5; // Assignment stays in place console.log(myVar); // 5
let and const Hoisting
javascript// let and const are hoisted but not initialized console.log(myLet); // ReferenceError: Cannot access before initialization let myLet = 10; console.log(myConst); // ReferenceError: Cannot access before initialization const myConst = 20; // This is called the "Temporal Dead Zone" function example() { // TDZ starts here console.log(x); // ReferenceError let x = 5; // TDZ ends here console.log(x); // 5 }
Function Hoisting
Function Declarations
javascript// Function declarations are fully hoisted sayHello(); // "Hello!" - Works before declaration function sayHello() { console.log("Hello!"); } // Another example console.log(add(2, 3)); // 5 - Works! function add(a, b) { return a + b; }
Function Expressions
javascript// Function expressions are NOT hoisted sayGoodbye(); // TypeError: sayGoodbye is not a function var sayGoodbye = function() { console.log("Goodbye!"); }; // Arrow functions are also not hoisted multiply(2, 3); // TypeError: multiply is not a function const multiply = (a, b) => a * b;
Hoisting in Different Scopes
Global Scope
javascript// Global hoisting console.log(globalVar); // undefined var globalVar = "I'm global"; console.log(globalFunc()); // "Global function" function globalFunc() { return "Global function"; }
Function Scope
javascriptfunction example() { console.log(localVar); // undefined (hoisted within function) var localVar = "I'm local"; console.log(innerFunc()); // "Inner function" function innerFunc() { return "Inner function"; } } example();
Block Scope
javascriptfunction blockScopeExample() { console.log(x); // undefined (var is function-scoped) if (true) { var x = 1; let y = 2; const z = 3; } console.log(x); // 1 (var is accessible) // console.log(y); // ReferenceError (let is block-scoped) // console.log(z); // ReferenceError (const is block-scoped) }
Practical Examples
1. Common Hoisting Mistakes
javascript// Mistake 1: Assuming let/const work like var function mistake1() { console.log(name); // ReferenceError let name = "John"; } // Mistake 2: Function expression hoisting confusion function mistake2() { console.log(typeof myFunc); // "undefined" console.log(myFunc); // undefined var myFunc = function() { return "Hello"; }; console.log(typeof myFunc); // "function" } // Mistake 3: Loop variable hoisting function mistake3() { for (var i = 0; i < 3; i++) { setTimeout(function() { console.log(i); // 3, 3, 3 (not 0, 1, 2) }, 100); } console.log(i); // 3 (i is accessible outside loop) }
2. Hoisting Best Practices
javascript// Good: Declare variables at the top function goodExample() { let name, age, city; // Declare at top name = "Alice"; age = 25; city = "New York"; return `${name}, ${age}, from ${city}`; } // Good: Use let/const to avoid hoisting confusion function betterExample() { const users = []; let currentUser = null; // Function logic here for (let i = 0; i < 5; i++) { // i is properly scoped to this block users.push(`User ${i}`); } return users; }
Temporal Dead Zone (TDZ)
javascriptfunction temporalDeadZoneExample() { // TDZ starts here for 'x' console.log(typeof x); // ReferenceError (not "undefined"!) let x = 5; // TDZ ends here console.log(x); // 5 } // TDZ with parameters function parameterTDZ(a = b, b = 2) { // ReferenceError: Cannot access 'b' before initialization return a + b; } // Fixed version function parameterFixed(a = 1, b = a + 1) { return a + b; // Works fine }
Hoisting with Classes
javascript// Class declarations are hoisted but not initialized console.log(MyClass); // ReferenceError class MyClass { constructor(name) { this.name = name; } } // Class expressions behave like function expressions console.log(MyClassExpr); // undefined var MyClassExpr = class { constructor(name) { this.name = name; } };
Interview Questions About Hoisting
javascript// Question 1: What does this output? var a = 1; function test() { console.log(a); // undefined (not 1!) var a = 2; console.log(a); // 2 } test(); // Explanation: Local 'a' is hoisted, shadowing global 'a' // Question 2: What happens here? function question2() { console.log(foo); // function foo() { return "second"; } var foo = "first"; function foo() { return "second"; } console.log(foo); // "first" } // Explanation: Function declarations are hoisted above var declarations // Question 3: Fix this code function fixMe() { for (var i = 0; i < 3; i++) { setTimeout(function() { console.log(i); // Prints 3, 3, 3 }, 100); } } // Solution 1: Use let function fixed1() { for (let i = 0; i < 3; i++) { setTimeout(function() { console.log(i); // Prints 0, 1, 2 }, 100); } } // Solution 2: Use closure function fixed2() { for (var i = 0; i < 3; i++) { (function(j) { setTimeout(function() { console.log(j); // Prints 0, 1, 2 }, 100); })(i); } }
Best Practices
javascript// 1. Use let and const instead of var function modernApproach() { const config = { theme: "dark" }; let currentUser = null; // Variables are not hoisted in confusing ways } // 2. Declare functions before using them function declareFirst() { // Good: Function is declared before use const result = calculateTotal(items); function calculateTotal(items) { return items.reduce((sum, item) => sum + item.price, 0); } } // 3. Initialize variables when declaring function initializeWhenDeclaring() { const userName = getCurrentUser()?.name || "Guest"; let itemCount = 0; // Clear intent, no hoisting confusion } // 4. Use strict mode to catch hoisting issues "use strict"; function strictMode() { // undeclaredVar = 5; // ReferenceError in strict mode let declaredVar = 5; // Proper declaration }