JavaScript Hoisting: Complete Guide for Developers
Master JavaScript hoisting: execution context, memory creation phase, var/let/const hoisting, Temporal Dead Zone (TDZ), function hoisting, and best practices with detailed examples.
JavaScript Hoisting: Complete Guide for Developers
π Table of Contents
- What is Hoisting?
- Why Do We Need Hoisting?
- Real-World Use Cases
- Syntax
- Internal Working
- Memory Diagram
- Data Flow Diagram
- Code Examples
- Common Mistakes
- Performance Considerations
- Interview Questions
- Cheat Sheet
- Summary
1. What is Hoisting?
Hoisting is JavaScript's behavior of moving variable and function declarations to the top of their scope during the compilation phase.
Simple Definition
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HOISTING DEFINITION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Hoisting is the process where JavaScript allocates memory
for variables and functions BEFORE executing any code.
Key Point:
ββββββββββ
Declarations are processed before code execution,
but assignments remain in place.
Common Misconception:
ββββββββββββββββββββ
β Code is physically moved to the top
β
Memory is allocated during creation phase
Visual Representation
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β EXECUTION CONTEXT PHASES β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
JavaScript Code
β
βββββββββββββββββββββββββββββββββββ
β Phase 1: Memory Creation β
β (Hoisting happens here) β
β β
β β’ Scan entire code β
β β’ Allocate memory for: β
β - Variables (undefined/TDZ) β
β - Functions (entire function) β
βββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββ
β Phase 2: Code Execution β
β β
β β’ Execute line by line β
β β’ Assign values to variables β
β β’ Execute function calls β
βββββββββββββββββββββββββββββββββββ
Hoisting Characteristics
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β WHAT GETS HOISTED? β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β var declarations β Hoisted with undefined
β let declarations β Hoisted to TDZ
β const declarations β Hoisted to TDZ
β Function declarations β Fully hoisted
β Class declarations β Hoisted to TDZ
β Function expressions β Only variable hoisted
β Arrow functions β Only variable hoisted
β Variable assignments β NOT hoisted
2. Why Do We Need Hoisting?
Hoisting exists due to JavaScript's two-phase execution model.
Problem Without Hoisting
// Imagine if JavaScript executed strictly line by line
function outer() {
inner(); // Would fail - inner not defined yet
function inner() {
console.log("Hello");
}
}
// With hoisting, this works!
outer(); // "Hello"
Benefits
- Function Declarations Available Everywhere: Call functions before they're defined
- Predictable Execution: Two-phase model ensures consistency
- Mutual Recursion: Functions can call each other regardless of order
- Code Organization: Flexibility in code structure
- Backward Compatibility: Maintains JavaScript's original behavior
The Two-Phase Model
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β WHY TWO PHASES? β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Phase 1: Memory Creation (Compilation)
ββββββββββββββββββββββββββββββββββββββ
β’ Parse entire code
β’ Create execution context
β’ Allocate memory for declarations
β’ Set up scope chain
β’ Determine 'this' binding
Phase 2: Code Execution (Runtime)
ββββββββββββββββββββββββββββββββββ
β’ Execute code line by line
β’ Assign values to variables
β’ Execute function calls
β’ Perform operations
This separation allows JavaScript to:
ββββββββββββββββββββββββββββββββββββ
β Optimize code execution
β Handle circular dependencies
β Support function declarations anywhere
β Implement closures effectively
3. Real-World Use Cases
1. Function Organization
// Main logic at top (readable)
function processUserData(user) {
if (!validateUser(user)) {
return handleError("Invalid user");
}
const processed = transformData(user);
return saveToDatabase(processed);
}
// Helper functions below (organized)
function validateUser(user) {
return user && user.id && user.name;
}
function transformData(user) {
return {
...user,
timestamp: Date.now()
};
}
function handleError(message) {
console.error(message);
return null;
}
function saveToDatabase(data) {
// Save logic
return data;
}
// Works due to function hoisting!
processUserData({ id: 1, name: "John" });
2. Mutual Recursion
// These functions call each other
function isEven(n) {
if (n === 0) return true;
return isOdd(n - 1);
}
function isOdd(n) {
if (n === 0) return false;
return isEven(n - 1);
}
// Works because both are hoisted
console.log(isEven(4)); // true
console.log(isOdd(3)); // true
3. Configuration Pattern
// Use config before definition
initializeApp(CONFIG);
// Config defined later (hoisted)
var CONFIG = {
apiUrl: "https://api.example.com",
timeout: 5000
};
function initializeApp(config) {
// config is undefined here due to hoisting!
// This is why we should avoid this pattern
console.log(config); // undefined
}
4. Module Pattern with Hoisting
var UserModule = (function() {
// Public API uses private functions
return {
createUser: createUser,
validateUser: validateUser
};
// Private functions defined after return (hoisted)
function createUser(name, email) {
if (!validateUser(email)) {
throw new Error("Invalid email");
}
return { name, email };
}
function validateUser(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
})();
4. Syntax
var Hoisting
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// VAR HOISTING
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// What you write:
console.log(name); // undefined (not ReferenceError!)
var name = "John";
console.log(name); // "John"
// How JavaScript interprets it:
var name; // Declaration hoisted
console.log(name); // undefined
name = "John"; // Assignment stays in place
console.log(name); // "John"
// Multiple var declarations
console.log(a, b, c); // undefined undefined undefined
var a = 1;
var b = 2;
var c = 3;
let Hoisting
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// LET HOISTING (with TDZ)
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// What you write:
console.log(age); // ReferenceError: Cannot access before initialization
let age = 25;
// What happens:
// let age; β Hoisted to TDZ (Temporal Dead Zone)
// console.log(age); β Error! Still in TDZ
// age = 25; β Initialization (exits TDZ)
// TDZ Example
{
// TDZ starts
console.log(x); // ReferenceError
console.log(y); // ReferenceError
let x = 10; // TDZ ends for x
const y = 20; // TDZ ends for y
console.log(x, y); // 10 20
}
const Hoisting
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// CONST HOISTING (with TDZ)
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// What you write:
console.log(PI); // ReferenceError
const PI = 3.14159;
// const behaves like let regarding hoisting
// Hoisted to TDZ, must be initialized before use
// const requires initialization
const VALUE; // SyntaxError: Missing initializer
const VALUE = 100; // Correct
Function Declaration Hoisting
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// FUNCTION DECLARATION HOISTING
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Call before definition - works!
greet("John"); // "Hello, John!"
function greet(name) {
console.log(`Hello, ${name}!`);
}
// Entire function is hoisted
// Memory Creation Phase:
// greet β [Function Object]
// Multiple function declarations
sayHi(); // "Hi!"
sayBye(); // "Bye!"
function sayHi() {
console.log("Hi!");
}
function sayBye() {
console.log("Bye!");
}
Function Expression Hoisting
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// FUNCTION EXPRESSION HOISTING
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// var function expression
greet(); // TypeError: greet is not a function
var greet = function() {
console.log("Hello");
};
// How it's hoisted:
// var greet; β Variable hoisted (undefined)
// greet(); β Error! greet is undefined
// greet = function() { ... }; β Assignment
// let/const function expression
sayHi(); // ReferenceError
const sayHi = function() {
console.log("Hi");
};
// Arrow function (same as function expression)
sayBye(); // ReferenceError
const sayBye = () => {
console.log("Bye");
};
Class Hoisting
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// CLASS HOISTING (with TDZ)
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Class declaration
const user = new User(); // ReferenceError
class User {
constructor(name) {
this.name = name;
}
}
// Classes are hoisted to TDZ like let/const
// Must be defined before use
5. Internal Working
Execution Context Creation
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β EXECUTION CONTEXT LIFECYCLE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Code:
βββββ
var x = 10;
let y = 20;
const z = 30;
function greet() {
console.log("Hello");
}
Step 1: Creation Phase (Hoisting)
ββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββ
β Global Execution Context β
β β
β Variable Environment: β
β ββ x: undefined β
β ββ greet: [Function] β
β β
β Lexical Environment: β
β ββ y: <uninitialized> (TDZ) β
β ββ z: <uninitialized> (TDZ) β
ββββββββββββββββββββββββββββββββββββ
Step 2: Execution Phase
βββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββ
β Global Execution Context β
β β
β Variable Environment: β
β ββ x: 10 β
β ββ greet: [Function] β
β β
β Lexical Environment: β
β ββ y: 20 β
β ββ z: 30 β
ββββββββββββββββββββββββββββββββββββ
Temporal Dead Zone (TDZ)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TEMPORAL DEAD ZONE EXPLAINED β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Code:
βββββ
{
// TDZ starts for 'value'
console.log(value); // ReferenceError
let value = 100; // TDZ ends
console.log(value); // 100
}
Timeline:
βββββββββ
βββββββββββββββββββββββββββββββββββββββ
β Block Scope Entered β
β β β
β TDZ Begins for 'value' β
β ββ Variable exists in memory β
β ββ But cannot be accessed β
β ββ Any access = ReferenceError β
β β β
β let value = 100; β
β β β
β TDZ Ends β
β ββ Variable initialized β
β ββ Now accessible β
βββββββββββββββββββββββββββββββββββββββ
Why TDZ Exists:
βββββββββββββββ
β Prevents use before initialization
β Catches programming errors early
β Makes const truly constant
β Encourages better code practices
6. Memory Diagram
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HOISTING MEMORY LAYOUT β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Code:
βββββ
console.log(a); // undefined
console.log(b); // ReferenceError
console.log(c); // ReferenceError
greet(); // "Hello"
test(); // TypeError
var a = 10;
let b = 20;
const c = 30;
function greet() {
console.log("Hello");
}
var test = function() {
console.log("Test");
};
Memory Creation Phase:
ββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββ
β Global Memory β
β β
β Variable Environment (var): β
β ββ a: undefined β
β ββ test: undefined β
β ββ greet: [Function Object] β
β β
β Lexical Environment (let/const): β
β ββ b: <uninitialized> (TDZ) β
β ββ c: <uninitialized> (TDZ) β
βββββββββββββββββββββββββββββββββββββββ
After Execution:
ββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββ
β Global Memory β
β β
β Variable Environment: β
β ββ a: 10 β
β ββ test: [Function] β
β ββ greet: [Function Object] β
β β
β Lexical Environment: β
β ββ b: 20 β
β ββ c: 30 β
βββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FUNCTION EXECUTION CONTEXT β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Code:
βββββ
function outer() {
console.log(x); // undefined
var x = 10;
console.log(y); // ReferenceError
let y = 20;
}
outer();
Memory for outer():
βββββββββββββββββββ
Creation Phase:
βββββββββββββββββββββββββββββββββββββββ
β outer() Execution Context β
β β
β Variable Environment: β
β ββ x: undefined β
β β
β Lexical Environment: β
β ββ y: <uninitialized> (TDZ) β
βββββββββββββββββββββββββββββββββββββββ
Execution Phase:
βββββββββββββββββββββββββββββββββββββββ
β outer() Execution Context β
β β
β Variable Environment: β
β ββ x: 10 β
β β
β Lexical Environment: β
β ββ y: 20 β
βββββββββββββββββββββββββββββββββββββββ
7. Data Flow Diagram
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HOISTING EXECUTION FLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
JavaScript File Loaded
β
ββββββββββββββββββββ
β Create Global β
β Execution Contextβ
ββββββββββββββββββββ
β
ββββββββββββββββββββ
β CREATION PHASE β
β (Hoisting) β
ββββββββββββββββββββ
β
ββββββββββββββββββββ
β Scan Code β
ββββββββββββββββββββ
β
ββββββββββββββββββββ
β Find var β
β declarations β
ββββββββββββββββββββ
β
ββββββββββββββββββββ
β Allocate memory β
β (undefined) β
ββββββββββββββββββββ
β
ββββββββββββββββββββ
β Find let/const β
β declarations β
ββββββββββββββββββββ
β
ββββββββββββββββββββ
β Allocate memory β
β (TDZ) β
ββββββββββββββββββββ
β
ββββββββββββββββββββ
β Find function β
β declarations β
ββββββββββββββββββββ
β
ββββββββββββββββββββ
β Store entire β
β function β
ββββββββββββββββββββ
β
ββββββββββββββββββββ
β EXECUTION PHASE β
ββββββββββββββββββββ
β
ββββββββββββββββββββ
β Execute line by β
β line β
ββββββββββββββββββββ
β
ββββββββββββββββββββ
β Assign values β
β to variables β
ββββββββββββββββββββ
β
ββββββββββββββββββββ
β Execute function β
β calls β
ββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β VARIABLE ACCESS FLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Variable Reference
β
ββββββββββββββββββββ
β Check if exists β
β in memory β
ββββββββββββββββββββ
β
ββββββ΄βββββ
β Exists? β
ββββββ¬βββββ
Yes β No
β β
β ReferenceError
β
ββββββββββββββββββββ
β Check type β
ββββββββββββββββββββ
β
ββββββ΄βββββ
β var? β
ββββββ¬βββββ
Yes β No
β β
Return ββββββββββββββββββββ
Value β let/const? β
ββββββββββββββββββββ
β
ββββββ΄βββββ
β In TDZ? β
ββββββ¬βββββ
Yes β No
β β
ReferenceError Return
Value
8. Code Examples
Example 1: var vs let vs const Hoisting
// var hoisting
console.log(varVariable); // undefined
var varVariable = "I'm var";
console.log(varVariable); // "I'm var"
// let hoisting (TDZ)
try {
console.log(letVariable); // ReferenceError
} catch (e) {
console.log("Error:", e.message);
}
let letVariable = "I'm let";
console.log(letVariable); // "I'm let"
// const hoisting (TDZ)
try {
console.log(constVariable); // ReferenceError
} catch (e) {
console.log("Error:", e.message);
}
const constVariable = "I'm const";
console.log(constVariable); // "I'm const"
Example 2: Function Hoisting Patterns
// Function declaration - fully hoisted
greet("John"); // "Hello, John!" (works!)
function greet(name) {
console.log(`Hello, ${name}!`);
}
// Function expression with var
try {
sayHi(); // TypeError: sayHi is not a function
} catch (e) {
console.log("Error:", e.message);
}
var sayHi = function() {
console.log("Hi!");
};
sayHi(); // "Hi!" (works after assignment)
// Function expression with const
try {
sayBye(); // ReferenceError
} catch (e) {
console.log("Error:", e.message);
}
const sayBye = function() {
console.log("Bye!");
};
sayBye(); // "Bye!" (works after initialization)
// Arrow function
try {
arrow(); // ReferenceError
} catch (e) {
console.log("Error:", e.message);
}
const arrow = () => {
console.log("Arrow!");
};
arrow(); // "Arrow!" (works after initialization)
Example 3: TDZ in Different Scopes
// Global scope TDZ
console.log("Before TDZ");
try {
console.log(globalVar); // ReferenceError
} catch (e) {
console.log("TDZ Error:", e.message);
}
let globalVar = "Global";
console.log(globalVar); // "Global"
// Block scope TDZ
{
console.log("Inside block");
try {
console.log(blockVar); // ReferenceError
} catch (e) {
console.log("Block TDZ Error:", e.message);
}
let blockVar = "Block";
console.log(blockVar); // "Block"
}
// Function scope TDZ
function testTDZ() {
console.log("Inside function");
try {
console.log(funcVar); // ReferenceError
} catch (e) {
console.log("Function TDZ Error:", e.message);
}
let funcVar = "Function";
console.log(funcVar); // "Function"
}
testTDZ();
Example 4: Hoisting in Loops
// var in loop - hoisted to function scope
console.log(i); // undefined (hoisted)
for (var i = 0; i < 3; i++) {
console.log(`Loop ${i}`);
}
console.log(i); // 3 (accessible outside loop)
// let in loop - block scoped
try {
console.log(j); // ReferenceError
} catch (e) {
console.log("Error:", e.message);
}
for (let j = 0; j < 3; j++) {
console.log(`Loop ${j}`);
}
try {
console.log(j); // ReferenceError (not accessible)
} catch (e) {
console.log("Error:", e.message);
}
Example 5: Complex Hoisting Scenario
// Complex example showing all hoisting behaviors
console.log("=== Complex Hoisting Example ===");
// 1. var hoisting
console.log(a); // undefined
var a = 10;
// 2. Function declaration hoisting
multiply(5, 3); // 15
function multiply(x, y) {
console.log(x * y);
}
// 3. let/const TDZ
try {
console.log(b); // ReferenceError
} catch (e) {
console.log("TDZ Error for b");
}
let b = 20;
// 4. Function expression hoisting
try {
divide(10, 2); // TypeError
} catch (e) {
console.log("Function expression not ready");
}
var divide = function(x, y) {
console.log(x / y);
};
divide(10, 2); // 5 (works after assignment)
// 5. Nested function hoisting
function outer() {
console.log("Outer function");
inner(); // Works! inner is hoisted
function inner() {
console.log("Inner function");
}
}
outer();
9. Common Mistakes
Mistake 1: Assuming let/const Are Not Hoisted
// β Wrong assumption: let/const are not hoisted
// They ARE hoisted, but to TDZ
// This proves let is hoisted:
let x = "outer";
{
console.log(x); // ReferenceError (not "outer")
// If let wasn't hoisted, it would print "outer"
let x = "inner";
}
// β
Correct understanding: let/const are hoisted to TDZ
let y = "outer";
{
// y is hoisted here but in TDZ
// console.log(y); // ReferenceError
let y = "inner";
console.log(y); // "inner"
}
Mistake 2: Relying on var Hoisting
// β Wrong: Relying on hoisting behavior
function badExample() {
console.log(user); // undefined (confusing!)
if (true) {
var user = "John";
}
console.log(user); // "John"
}
// β
Correct: Declare variables at the top
function goodExample() {
let user;
if (true) {
user = "John";
}
console.log(user); // "John" (clear intent)
}
Mistake 3: Function Expression Timing
// β Wrong: Calling function expression before definition
try {
calculate(5, 3); // TypeError
} catch (e) {
console.log("Error:", e.message);
}
var calculate = function(a, b) {
return a + b;
};
// β
Correct: Use function declaration or call after definition
function calculate(a, b) {
return a + b;
}
calculate(5, 3); // 8 (works!)
Mistake 4: TDZ in Default Parameters
// β Wrong: Using parameter in its own default value
function wrong(a = b, b = 2) { // ReferenceError
return a + b;
}
// β
Correct: Parameters are evaluated left to right
function correct(a = 1, b = a + 1) {
return a + b;
}
console.log(correct()); // 3
Mistake 5: Hoisting in Conditionals
// β Wrong: Assuming conditional prevents hoisting
if (false) {
var x = 10; // Still hoisted!
}
console.log(x); // undefined (not ReferenceError)
// β
Correct: Use let/const for block scoping
if (false) {
let y = 10;
}
try {
console.log(y); // ReferenceError
} catch (e) {
console.log("y is not accessible");
}
10. Performance Considerations
1. Avoid Excessive var Usage
// β Slow: var creates function-scoped variables
function processArray(arr) {
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
// var creates unnecessary scope pollution
}
// i and item still accessible here
}
// β
Fast: let creates block-scoped variables
function processArray(arr) {
for (let i = 0; i < arr.length; i++) {
const item = arr[i];
// Garbage collected after each iteration
}
// i and item not accessible here
}
2. Function Declaration vs Expression
// β
Faster: Function declarations are optimized
function add(a, b) {
return a + b;
}
// Slower: Function expressions create closures
const subtract = function(a, b) {
return a - b;
};
3. Minimize TDZ Checks
// β Inefficient: Multiple TDZ checks
function inefficient() {
// Each access checks TDZ
console.log(getValue());
console.log(getValue());
console.log(getValue());
function getValue() {
return value; // TDZ check
}
let value = 100;
}
// β
Efficient: Single TDZ check
function efficient() {
let value = 100; // Initialize early
function getValue() {
return value; // No TDZ check
}
console.log(getValue());
console.log(getValue());
console.log(getValue());
}
11. Interview Questions
Q1: What is hoisting in JavaScript?
Answer: Hoisting is JavaScript's behavior of moving variable and function declarations to the top of their scope during the compilation phase. More accurately, it's the process where JavaScript allocates memory for declarations before executing code.
// What you write:
console.log(x); // undefined
var x = 10;
// How JavaScript processes it:
var x; // Declaration hoisted
console.log(x); // undefined
x = 10; // Assignment stays
Q2: Are let and const hoisted?
Answer: Yes, let and const ARE hoisted, but they're placed in the Temporal Dead Zone (TDZ) and cannot be accessed before initialization, unlike var which is initialized with undefined.
// var - hoisted with undefined
console.log(a); // undefined
var a = 10;
// let - hoisted to TDZ
console.log(b); // ReferenceError
let b = 20;
// const - hoisted to TDZ
console.log(c); // ReferenceError
const c = 30;
Q3: What is the Temporal Dead Zone (TDZ)?
Answer: The TDZ is the period between entering a scope where a let/const variable is declared and the actual initialization of that variable. During this time, the variable exists but cannot be accessed.
{
// TDZ starts for 'value'
console.log(value); // ReferenceError
let value = 100; // TDZ ends here
console.log(value); // 100 (accessible)
}
Q4: Why do function declarations work before they're defined?
Answer: Function declarations are fully hoisted - the entire function object is stored in memory during the creation phase, making them available throughout their scope.
greet(); // "Hello!" (works!)
function greet() {
console.log("Hello!");
}
// Function expressions don't work this way:
sayHi(); // TypeError
var sayHi = function() {
console.log("Hi!");
};
Q5: What's the difference between function declaration and function expression hoisting?
Answer:
- Function Declaration: Entire function is hoisted and can be called before definition
- Function Expression: Only the variable is hoisted (as undefined or TDZ), not the function
// Function Declaration
console.log(typeof add); // "function"
function add(a, b) {
return a + b;
}
// Function Expression with var
console.log(typeof subtract); // "undefined"
var subtract = function(a, b) {
return a - b;
};
// Function Expression with const
console.log(typeof multiply); // ReferenceError
const multiply = function(a, b) {
return a * b;
};
Q6: How does hoisting work in nested functions?
Answer: Each function creates its own execution context with its own hoisting phase. Variables and functions are hoisted within their respective scopes.
function outer() {
console.log(x); // undefined (hoisted in outer)
var x = 10;
function inner() {
console.log(y); // undefined (hoisted in inner)
var y = 20;
}
inner();
}
outer();
12. Cheat Sheet
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// HOISTING QUICK REFERENCE
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// EXECUTION PHASES
// ββββββββββββββββ
1. Creation Phase (Hoisting)
β’ Allocate memory for declarations
β’ Set up scope chain
β’ Determine 'this'
2. Execution Phase
β’ Execute code line by line
β’ Assign values
β’ Execute functions
// VARIABLE HOISTING
// βββββββββββββββββ
var x; β Hoisted with undefined
let y; β Hoisted to TDZ
const z; β Hoisted to TDZ
// FUNCTION HOISTING
// βββββββββββββββββ
function f() {} β Fully hoisted
var f = function() {}; β Variable hoisted (undefined)
const f = function() {}; β Variable hoisted (TDZ)
const f = () => {}; β Variable hoisted (TDZ)
// TDZ (TEMPORAL DEAD ZONE)
// ββββββββββββββββββββββββ
{
// TDZ starts
console.log(x); // ReferenceError
let x = 10; // TDZ ends
}
// HOISTING BEHAVIOR
// βββββββββββββββββ
Declaration | Hoisted | Initial Value | Accessible Before Init
βββββββββββββ|βββββββββ|βββββββββββββββ|ββββββββββββββββββββββ
var | Yes | undefined | Yes (undefined)
let | Yes | TDZ | No (ReferenceError)
const | Yes | TDZ | No (ReferenceError)
function | Yes | Function | Yes (callable)
class | Yes | TDZ | No (ReferenceError)
// COMMON PATTERNS
// βββββββββββββββ
// β
Good: Function declaration
function add(a, b) {
return a + b;
}
// β
Good: Variables at top
function process() {
let x = 10;
const y = 20;
// ... use x and y
}
// β Bad: Relying on hoisting
function bad() {
console.log(x); // undefined (confusing)
var x = 10;
}
// BEST PRACTICES
// ββββββββββββββ
β Use const by default
β Use let when reassignment needed
β Avoid var
β Declare variables at top of scope
β Use function declarations for utilities
β Initialize variables before use
β Don't rely on hoisting behavior
β Understand TDZ to avoid errors
13. Summary
Key Takeaways
β
Hoisting = Memory allocation before execution
β
Two phases: Creation (hoisting) and Execution
β
var hoisted with undefined
β
let/const hoisted to TDZ
β
Function declarations fully hoisted
β
Function expressions only variable hoisted
β
TDZ prevents access before initialization
β
Each scope has its own hoisting phase
β
Not physical movement of code
β
Understanding hoisting prevents bugs
Best Practices
- Use const by default, let when needed
- Avoid var in modern JavaScript
- Declare variables at the top of their scope
- Initialize variables before use
- Use function declarations for utilities
- Understand TDZ to avoid ReferenceErrors
- Don't rely on hoisting behavior
- Write clear code that doesn't depend on hoisting
- Use strict mode to catch errors early
- Test edge cases involving hoisting
Hoisting is a fundamental JavaScript concept that explains many seemingly strange behaviors. Understanding it is essential for writing predictable, bug-free code and for mastering execution context, scope, and closures.