Full Stack • Java • System Design • Cloud • AI Engineering

JavaScript2026-06-06

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

  1. What is Hoisting?
  2. Why Do We Need Hoisting?
  3. Real-World Use Cases
  4. Syntax
  5. Internal Working
  6. Memory Diagram
  7. Data Flow Diagram
  8. Code Examples
  9. Common Mistakes
  10. Performance Considerations
  11. Interview Questions
  12. Cheat Sheet
  13. 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

  1. Function Declarations Available Everywhere: Call functions before they're defined
  2. Predictable Execution: Two-phase model ensures consistency
  3. Mutual Recursion: Functions can call each other regardless of order
  4. Code Organization: Flexibility in code structure
  5. 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

  1. Use const by default, let when needed
  2. Avoid var in modern JavaScript
  3. Declare variables at the top of their scope
  4. Initialize variables before use
  5. Use function declarations for utilities
  6. Understand TDZ to avoid ReferenceErrors
  7. Don't rely on hoisting behavior
  8. Write clear code that doesn't depend on hoisting
  9. Use strict mode to catch errors early
  10. 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.