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

JavaScript2026-06-06

JavaScript Scope: Complete Guide for Developers

Master JavaScript scope: global, function, and block scope, lexical scope, scope chain, variable shadowing, closures, and best practices with detailed examples.

JavaScript Scope: Complete Guide for Developers


📋 Table of Contents

  1. What is Scope?
  2. Why Do We Need Scope?
  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 Scope?

Scope determines the accessibility and visibility of variables, functions, and objects in your code during runtime.

Scope Types

┌─────────────────────────────────────────────────────────────┐
│         JAVASCRIPT SCOPE HIERARCHY                           │
└─────────────────────────────────────────────────────────────┘

                    Scope
                      |
        ┌─────────────┼─────────────┐
        |             |             |
   Global Scope  Function Scope  Block Scope
        |             |             |
    Accessible    Accessible    Accessible
    Everywhere    in Function   in Block
        |             |             |
    var, let,     var, let,     let, const
    const, fn     const, fn     (ES6+)

Scope Characteristics

┌─────────────────────────────────────────────────────────────┐
│         SCOPE CHARACTERISTICS                                │
└─────────────────────────────────────────────────────────────┘

Scope Properties:
─────────────────
1. Determines variable accessibility
2. Controls variable lifetime
3. Prevents naming conflicts
4. Enables encapsulation
5. Supports closures
6. Affects memory management

Scope Rules:
────────────
• Inner scopes can access outer scopes
• Outer scopes CANNOT access inner scopes
• Scope is determined at write-time (lexical)
• Variables shadow outer variables with same name

2. Why Do We Need Scope?

Scope solves critical programming problems.

Without Scope

// Imagine everything is global
var username = "John";
var password = "secret123";
var apiKey = "abc123xyz";
var tempData = [1, 2, 3];

// Problems:
// 1. Naming conflicts
// 2. Security issues
// 3. Memory waste
// 4. Unpredictable behavior
// 5. Hard to maintain

With Scope

// Encapsulated and secure
function authenticateUser() {
    const username = "John";  // Function scope
    const password = "secret123";  // Function scope
    
    if (username && password) {
        const token = generateToken();  // Block scope
        return token;
    }
}

// username, password, token are not accessible here

Benefits

  1. Encapsulation: Hide implementation details
  2. Security: Protect sensitive data
  3. Memory Management: Variables cleaned up when scope ends
  4. Naming Freedom: Reuse variable names in different scopes
  5. Modularity: Create independent code modules
  6. Predictability: Clear variable lifetime

3. Real-World Use Cases

1. Module Pattern

const UserModule = (function() {
    // Private variables (function scope)
    let users = [];
    let currentUser = null;
    
    // Public API
    return {
        addUser(user) {
            users.push(user);
        },
        
        getUsers() {
            return [...users];  // Return copy
        },
        
        login(username) {
            currentUser = users.find(u => u.name === username);
            return currentUser !== null;
        },
        
        getCurrentUser() {
            return currentUser ? { ...currentUser } : null;
        }
    };
})();

// users and currentUser are private
UserModule.addUser({ name: "John", role: "admin" });
console.log(UserModule.getUsers());

2. Configuration Management

// Global configuration
const CONFIG = {
    API_URL: "https://api.example.com",
    TIMEOUT: 5000
};

function fetchData(endpoint) {
    // Function scope
    const url = `${CONFIG.API_URL}/${endpoint}`;
    const options = { timeout: CONFIG.TIMEOUT };
    
    // Block scope
    if (options.timeout > 3000) {
        const warning = "Long timeout detected";
        console.warn(warning);
    }
    
    return fetch(url, options);
}

3. Event Handlers

function setupEventHandlers() {
    const buttons = document.querySelectorAll('.btn');
    
    buttons.forEach((button, index) => {
        // Each iteration creates new block scope
        const buttonId = `btn-${index}`;
        
        button.addEventListener('click', () => {
            // Closure: accesses buttonId from outer scope
            console.log(`Button ${buttonId} clicked`);
        });
    });
}

4. Loop Scope

// ❌ Problem with var (function scope)
for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3

// ✅ Solution with let (block scope)
for (let i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100);
}
// Output: 0, 1, 2

4. Syntax

Global Scope

// ═══════════════════════════════════════════════════════════
// GLOBAL SCOPE VARIABLES
// ═══════════════════════════════════════════════════════════
// Declared outside all functions and blocks

var globalVar = "I'm global (var)";
let globalLet = "I'm global (let)";
const globalConst = "I'm global (const)";

function anyFunction() {
    console.log(globalVar);    // Accessible
    console.log(globalLet);    // Accessible
    console.log(globalConst);  // Accessible
}

// ═══════════════════════════════════════════════════════════
// IMPLICIT GLOBAL (without declaration)
// ═══════════════════════════════════════════════════════════
function createGlobal() {
    implicitGlobal = "Oops!";  // Creates global variable
}

createGlobal();
console.log(implicitGlobal);  // "Oops!" (accessible globally)

// ✅ Always use strict mode to prevent this
"use strict";
function createGlobal() {
    implicitGlobal = "Error!";  // ReferenceError in strict mode
}

Function Scope

// ═══════════════════════════════════════════════════════════
// FUNCTION SCOPE
// ═══════════════════════════════════════════════════════════
function myFunction() {
    var functionVar = "Function scoped";
    let functionLet = "Also function scoped";
    const functionConst = "Also function scoped";
    
    console.log(functionVar);    // Accessible
    console.log(functionLet);    // Accessible
    console.log(functionConst);  // Accessible
}

myFunction();

// ❌ Not accessible outside
console.log(functionVar);    // ReferenceError
console.log(functionLet);    // ReferenceError
console.log(functionConst);  // ReferenceError

// ═══════════════════════════════════════════════════════════
// NESTED FUNCTION SCOPE
// ═══════════════════════════════════════════════════════════
function outer() {
    const outerVar = "Outer";
    
    function inner() {
        const innerVar = "Inner";
        console.log(outerVar);  // Accessible (scope chain)
        console.log(innerVar);  // Accessible
    }
    
    inner();
    console.log(innerVar);  // ReferenceError
}

Block Scope

// ═══════════════════════════════════════════════════════════
// BLOCK SCOPE (let and const)
// ═══════════════════════════════════════════════════════════
{
    let blockLet = "Block scoped";
    const blockConst = "Block scoped";
    var blockVar = "NOT block scoped";
    
    console.log(blockLet);    // Accessible
    console.log(blockConst);  // Accessible
    console.log(blockVar);    // Accessible
}

console.log(blockLet);    // ReferenceError
console.log(blockConst);  // ReferenceError
console.log(blockVar);    // "NOT block scoped" (var ignores blocks)

// ═══════════════════════════════════════════════════════════
// IF BLOCK SCOPE
// ═══════════════════════════════════════════════════════════
if (true) {
    let x = 10;
    const y = 20;
    var z = 30;
}

console.log(x);  // ReferenceError
console.log(y);  // ReferenceError
console.log(z);  // 30 (var is function scoped)

// ═══════════════════════════════════════════════════════════
// LOOP BLOCK SCOPE
// ═══════════════════════════════════════════════════════════
for (let i = 0; i < 3; i++) {
    // Each iteration creates new block scope for 'i'
    setTimeout(() => console.log(i), 100);
}
// Output: 0, 1, 2

for (var j = 0; j < 3; j++) {
    // 'j' is function scoped, shared across iterations
    setTimeout(() => console.log(j), 100);
}
// Output: 3, 3, 3

// ═══════════════════════════════════════════════════════════
// SWITCH BLOCK SCOPE
// ═══════════════════════════════════════════════════════════
switch (value) {
    case 1: {
        let x = 10;  // Block scoped to this case
        break;
    }
    case 2: {
        let x = 20;  // Different x, different block
        break;
    }
}

5. Internal Working

Scope Chain Resolution

┌─────────────────────────────────────────────────────────────┐
│         SCOPE CHAIN RESOLUTION                               │
└─────────────────────────────────────────────────────────────┘

Code:
─────
const global = "Global";

function outer() {
    const outerVar = "Outer";
    
    function inner() {
        const innerVar = "Inner";
        console.log(global);     // Scope chain lookup
        console.log(outerVar);   // Scope chain lookup
        console.log(innerVar);   // Local scope
    }
    
    inner();
}

Scope Chain:
────────────
inner() Scope
    ↓ (not found, look up)
outer() Scope
    ↓ (not found, look up)
Global Scope
    ↓ (not found)
ReferenceError

Variable Lookup Process:
────────────────────────
1. console.log(innerVar)
   → Found in inner() scope ✓

2. console.log(outerVar)
   → Not in inner() scope
   → Look in outer() scope
   → Found ✓

3. console.log(global)
   → Not in inner() scope
   → Not in outer() scope
   → Look in global scope
   → Found ✓

Lexical Scope

┌─────────────────────────────────────────────────────────────┐
│         LEXICAL SCOPE (STATIC SCOPE)                         │
└─────────────────────────────────────────────────────────────┘

Lexical Scope = Scope determined by code structure

Code:
─────
const x = "global";

function outer() {
    const x = "outer";
    
    function inner() {
        console.log(x);  // Which x?
    }
    
    return inner;
}

const fn = outer();
fn();  // "outer" (not "global")

Why?
────
Scope is determined by WHERE the function is DEFINED,
not where it is CALLED.

inner() is defined inside outer(), so it has access
to outer()'s scope, regardless of where it's called.

Lexical Environment:
────────────────────
When inner() is created:
┌─────────────────────────────────────┐
│ inner() [[Scope]]                   │
│ ├─ Reference to outer() scope       │
│ │  └─ x: "outer"                    │
│ └─ Reference to global scope        │
│    └─ x: "global"                   │
└─────────────────────────────────────┘

This reference is PERMANENT (lexical binding)

6. Memory Diagram

┌─────────────────────────────────────────────────────────────┐
│         SCOPE MEMORY LAYOUT                                  │
└─────────────────────────────────────────────────────────────┘

Code:
─────
const global = "Global";

function outer() {
    const outerVar = "Outer";
    
    function inner() {
        const innerVar = "Inner";
    }
    
    inner();
}

outer();

Memory Structure:
─────────────────

Global Memory:
┌─────────────────────────────────────┐
│ global: "Global"                    │
│ outer: [Function]                   │
└─────────────────────────────────────┘

When outer() is called:
┌─────────────────────────────────────┐
│ outer() Execution Context           │
│ ├─ outerVar: "Outer"                │
│ ├─ inner: [Function]                │
│ └─ [[Scope]]: Global                │
└─────────────────────────────────────┘

When inner() is called:
┌─────────────────────────────────────┐
│ inner() Execution Context           │
│ ├─ innerVar: "Inner"                │
│ └─ [[Scope]]: outer() → Global      │
└─────────────────────────────────────┘

After inner() returns:
┌─────────────────────────────────────┐
│ outer() Execution Context           │
│ (inner() context destroyed)         │
└─────────────────────────────────────┘

After outer() returns:
┌─────────────────────────────────────┐
│ Global Memory                       │
│ (outer() context destroyed)         │
└─────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│         CLOSURE MEMORY (SCOPE PRESERVATION)                  │
└─────────────────────────────────────────────────────────────┘

Code:
─────
function createCounter() {
    let count = 0;
    
    return function() {
        count++;
        return count;
    };
}

const counter = createCounter();

Memory After createCounter() returns:
──────────────────────────────────────
┌─────────────────────────────────────┐
│ Global Memory                       │
│ ├─ counter: [Function]              │
│ │   └─ [[Scope]]: {                 │
│ │        count: 0  ← PRESERVED!     │
│ │      }                            │
└─────────────────────────────────────┘

After counter() called:
───────────────────────
┌─────────────────────────────────────┐
│ Global Memory                       │
│ ├─ counter: [Function]              │
│ │   └─ [[Scope]]: {                 │
│ │        count: 1  ← UPDATED!       │
│ │      }                            │
└─────────────────────────────────────┘

The closure keeps the scope alive!

7. Data Flow Diagram

┌─────────────────────────────────────────────────────────────┐
│         VARIABLE LOOKUP FLOW                                 │
└─────────────────────────────────────────────────────────────┘

    Variable Reference
           ↓
    ┌──────────────────┐
    │ Check Local Scope│
    └──────────────────┘
           ↓
      ┌────┴────┐
      │ Found?  │
      └────┬────┘
       Yes │ No
           ↓  ↓
      Return  ┌──────────────────────┐
      Value   │ Check Parent Scope   │
              └──────────────────────┘
                       ↓
                  ┌────┴────┐
                  │ Found?  │
                  └────┬────┘
                   Yes │ No
                       ↓  ↓
                  Return  ┌──────────────────────┐
                  Value   │ Check Global Scope   │
                          └──────────────────────┘
                                   ↓
                              ┌────┴────┐
                              │ Found?  │
                              └────┬────┘
                           Yes │ No
                               ↓  ↓
                          Return  ReferenceError
                          Value

┌─────────────────────────────────────────────────────────────┐
│         SCOPE CREATION FLOW                                  │
└─────────────────────────────────────────────────────────────┘

    Program Start
         ↓
    ┌─────────────────┐
    │ Create Global   │
    │ Execution       │
    │ Context         │
    └─────────────────┘
         ↓
    ┌─────────────────┐
    │ Global Scope    │
    │ Created         │
    └─────────────────┘
         ↓
    Function Called
         ↓
    ┌─────────────────┐
    │ Create Function │
    │ Execution       │
    │ Context         │
    └─────────────────┘
         ↓
    ┌─────────────────┐
    │ Function Scope  │
    │ Created         │
    │ (linked to      │
    │  parent scope)  │
    └─────────────────┘
         ↓
    Block Entered
         ↓
    ┌─────────────────┐
    │ Block Scope     │
    │ Created         │
    │ (for let/const) │
    └─────────────────┘
         ↓
    Block Exited
         ↓
    ┌─────────────────┐
    │ Block Scope     │
    │ Destroyed       │
    └─────────────────┘
         ↓
    Function Returns
         ↓
    ┌─────────────────┐
    │ Function Scope  │
    │ Destroyed       │
    │ (unless closure)│
    └─────────────────┘

8. Code Examples

Example 1: Scope Chain

const globalVar = "I'm global";

function level1() {
    const level1Var = "I'm in level 1";
    
    function level2() {
        const level2Var = "I'm in level 2";
        
        function level3() {
            const level3Var = "I'm in level 3";
            
            // Can access all outer scopes
            console.log(globalVar);   // "I'm global"
            console.log(level1Var);   // "I'm in level 1"
            console.log(level2Var);   // "I'm in level 2"
            console.log(level3Var);   // "I'm in level 3"
        }
        
        level3();
        // console.log(level3Var);  // ReferenceError
    }
    
    level2();
    // console.log(level2Var);  // ReferenceError
}

level1();
// console.log(level1Var);  // ReferenceError

Example 2: Variable Shadowing

const name = "Global Name";

function outer() {
    const name = "Outer Name";
    
    console.log(name);  // "Outer Name" (shadows global)
    
    function inner() {
        const name = "Inner Name";
        console.log(name);  // "Inner Name" (shadows outer)
    }
    
    inner();
    console.log(name);  // "Outer Name" (outer's name)
}

outer();
console.log(name);  // "Global Name" (global name)

// Accessing shadowed variables
function accessOuter() {
    const x = "outer";
    
    function accessInner() {
        const x = "inner";
        console.log(x);  // "inner"
        
        // No way to access outer 'x' directly
        // (it's shadowed)
    }
    
    accessInner();
}

Example 3: Closure with Scope

function createCounter(initialValue = 0) {
    let count = initialValue;  // Private variable
    
    return {
        increment() {
            count++;
            return count;
        },
        
        decrement() {
            count--;
            return count;
        },
        
        getCount() {
            return count;
        },
        
        reset() {
            count = initialValue;
        }
    };
}

const counter1 = createCounter(0);
const counter2 = createCounter(100);

console.log(counter1.increment());  // 1
console.log(counter1.increment());  // 2
console.log(counter2.increment());  // 101

console.log(counter1.getCount());   // 2
console.log(counter2.getCount());   // 101

// count is private - cannot access directly
console.log(counter1.count);  // undefined

Example 4: Block Scope in Loops

// Problem with var (function scope)
console.log("Using var:");
for (var i = 0; i < 3; i++) {
    setTimeout(() => {
        console.log(`var i: ${i}`);
    }, 100);
}
// Output: var i: 3, var i: 3, var i: 3

// Solution with let (block scope)
console.log("Using let:");
for (let j = 0; j < 3; j++) {
    setTimeout(() => {
        console.log(`let j: ${j}`);
    }, 100);
}
// Output: let j: 0, let j: 1, let j: 2

// Why? Each iteration creates new block scope for 'let'
// Equivalent to:
for (let k = 0; k < 3; k++) {
    // New block scope for each iteration
    let _k = k;  // Captured by closure
    setTimeout(() => {
        console.log(`let k: ${_k}`);
    }, 100);
}

Example 5: Module Pattern

const ShoppingCart = (function() {
    // Private variables and functions
    let items = [];
    let total = 0;
    
    function calculateTotal() {
        total = items.reduce((sum, item) => sum + item.price, 0);
    }
    
    function validateItem(item) {
        return item && item.name && item.price > 0;
    }
    
    // Public API
    return {
        addItem(item) {
            if (!validateItem(item)) {
                throw new Error("Invalid item");
            }
            items.push(item);
            calculateTotal();
            return this;
        },
        
        removeItem(itemName) {
            items = items.filter(item => item.name !== itemName);
            calculateTotal();
            return this;
        },
        
        getItems() {
            return [...items];  // Return copy
        },
        
        getTotal() {
            return total;
        },
        
        clear() {
            items = [];
            total = 0;
            return this;
        }
    };
})();

// Usage
ShoppingCart
    .addItem({ name: "Book", price: 20 })
    .addItem({ name: "Pen", price: 5 });

console.log(ShoppingCart.getTotal());  // 25
console.log(ShoppingCart.getItems());  // [{ name: "Book", price: 20 }, ...]

// Private variables are not accessible
console.log(ShoppingCart.items);  // undefined
console.log(ShoppingCart.total);  // undefined

9. Common Mistakes

Mistake 1: Accidental Global Variables

function createUser() {
    // ❌ Wrong: Creates global variable
    username = "John";  // No var/let/const
}

createUser();
console.log(username);  // "John" (global!)

// ✅ Correct: Use strict mode and declare variables
"use strict";

function createUser() {
    const username = "John";  // Properly scoped
}

createUser();
console.log(username);  // ReferenceError

Mistake 2: Using var in Loops

// ❌ Wrong: var is function scoped
for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3

// ✅ Correct: let is block scoped
for (let i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100);
}
// Output: 0, 1, 2

Mistake 3: Assuming Block Scope for var

// ❌ Wrong: var ignores block scope
if (true) {
    var x = 10;
}
console.log(x);  // 10 (accessible!)

// ✅ Correct: Use let or const
if (true) {
    let y = 10;
}
console.log(y);  // ReferenceError

Mistake 4: Excessive Global Variables

// ❌ Wrong: Polluting global scope
var user = "John";
var age = 30;
var email = "[email protected]";
var address = "123 Main St";

// ✅ Correct: Encapsulate in object or module
const userData = {
    user: "John",
    age: 30,
    email: "[email protected]",
    address: "123 Main St"
};

Mistake 5: Not Understanding Closure Scope

// ❌ Wrong: Expecting different values
function createFunctions() {
    const functions = [];
    
    for (var i = 0; i < 3; i++) {
        functions.push(function() {
            return i;
        });
    }
    
    return functions;
}

const fns = createFunctions();
console.log(fns[0]());  // 3 (not 0!)
console.log(fns[1]());  // 3 (not 1!)
console.log(fns[2]());  // 3 (not 2!)

// ✅ Correct: Use let or IIFE
function createFunctions() {
    const functions = [];
    
    for (let i = 0; i < 3; i++) {
        functions.push(function() {
            return i;
        });
    }
    
    return functions;
}

const fns = createFunctions();
console.log(fns[0]());  // 0
console.log(fns[1]());  // 1
console.log(fns[2]());  // 2

10. Performance Considerations

1. Scope Chain Length

// ❌ Slow: Deep scope chain
function level1() {
    function level2() {
        function level3() {
            function level4() {
                function level5() {
                    // Accessing global variable requires
                    // traversing 5 levels of scope chain
                    console.log(globalVar);
                }
                level5();
            }
            level4();
        }
        level3();
    }
    level2();
}

// ✅ Fast: Cache frequently accessed outer variables
function level1() {
    const cachedGlobal = globalVar;  // Cache it
    
    function level2() {
        // Use cached value (faster lookup)
        console.log(cachedGlobal);
    }
    level2();
}

2. Closure Memory

// ❌ Memory leak: Unnecessary closure
function createHandler() {
    const largeData = new Array(1000000).fill('data');
    
    return function() {
        console.log('Handler called');
        // largeData is kept in memory even though not used
    };
}

// ✅ Better: Don't capture unnecessary variables
function createHandler() {
    // largeData is local, will be garbage collected
    const largeData = new Array(1000000).fill('data');
    processData(largeData);
    
    return function() {
        console.log('Handler called');
        // No reference to largeData
    };
}

3. Global Scope Pollution

// ❌ Slow: Many global variables
var global1, global2, global3, /* ... */ global100;

// ✅ Fast: Single global namespace
const APP = {
    config: {},
    utils: {},
    data: {}
};

11. Interview Questions

Q1: What is scope in JavaScript?

Answer: Scope determines the accessibility and visibility of variables, functions, and objects in code. JavaScript has three types of scope: global, function, and block scope.

const global = "Global";  // Global scope

function myFunc() {
    const func = "Function";  // Function scope
    
    if (true) {
        const block = "Block";  // Block scope
    }
}

Q2: What's the difference between function scope and block scope?

Answer:

  • Function scope: Variables declared with var are accessible throughout the entire function
  • Block scope: Variables declared with let and const are accessible only within the block {}
function test() {
    if (true) {
        var funcScoped = "var";
        let blockScoped = "let";
    }
    console.log(funcScoped);   // "var" (accessible)
    console.log(blockScoped);  // ReferenceError
}

Q3: What is the scope chain?

Answer: The scope chain is the mechanism JavaScript uses to resolve variable references. It searches from the current scope outward to parent scopes until the variable is found or global scope is reached.

const global = "Global";

function outer() {
    const outerVar = "Outer";
    
    function inner() {
        console.log(global);    // Found in global scope
        console.log(outerVar);  // Found in outer scope
    }
}

Q4: What is lexical scope?

Answer: Lexical scope (static scope) means that scope is determined by where functions are defined in the code, not where they are called. JavaScript uses lexical scoping.

const x = "global";

function outer() {
    const x = "outer";
    
    function inner() {
        console.log(x);  // "outer" (lexically scoped)
    }
    
    return inner;
}

const fn = outer();
fn();  // "outer" (not "global")

Q5: What is variable shadowing?

Answer: Variable shadowing occurs when a variable in an inner scope has the same name as a variable in an outer scope, effectively "hiding" the outer variable.

const name = "Global";

function test() {
    const name = "Local";
    console.log(name);  // "Local" (shadows global)
}

test();
console.log(name);  // "Global"

Q6: Why does var behave differently in loops?

Answer: var is function-scoped, not block-scoped. In loops, there's only one var variable shared across all iterations, while let creates a new binding for each iteration.

// var: One variable, shared
for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3

// let: New variable per iteration
for (let i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100);
}
// Output: 0, 1, 2

12. Cheat Sheet

// ═══════════════════════════════════════════════════════════
// SCOPE QUICK REFERENCE
// ═══════════════════════════════════════════════════════════

// SCOPE TYPES
// ───────────
Global Scope    → Accessible everywhere
Function Scope  → Accessible within function
Block Scope     → Accessible within block {}

// VARIABLE SCOPE
// ──────────────
var    → Function scoped (avoid)
let    → Block scoped
const  → Block scoped (immutable binding)

// SCOPE RULES
// ───────────
✓ Inner scopes can access outer scopes
✗ Outer scopes CANNOT access inner scopes
✓ Scope determined at write-time (lexical)
✓ Variables shadow outer variables

// SCOPE CHAIN
// ───────────
Current Scope → Parent Scope → ... → Global Scope

// COMMON PATTERNS
// ───────────────
// Module pattern
const Module = (function() {
    const private = "private";
    return { public: "public" };
})();

// Closure
function outer() {
    const x = 1;
    return function() { return x; };
}

// Block scope in loops
for (let i = 0; i < 3; i++) {
    // New 'i' per iteration
}

// BEST PRACTICES
// ──────────────
✓ Use const by default
✓ Use let when reassignment needed
✗ Avoid var
✓ Minimize global variables
✓ Use strict mode
✓ Keep scope chains short
✓ Use closures for privacy
✗ Don't create accidental globals

// SCOPE COMPARISON
// ────────────────
Feature        | Global | Function | Block
───────────────|────────|──────────|──────
var            | Yes    | Yes      | No
let            | Yes    | Yes      | Yes
const          | Yes    | Yes      | Yes
Accessible     | Every  | In func  | In block
Lifetime       | Always | Call     | Block exec

13. Summary

Key Takeaways

Scope determines variable accessibility
Three types: Global, Function, Block
var is function-scoped (avoid)
let/const are block-scoped (prefer)
Scope chain resolves variables from inner to outer
Lexical scope determined by code structure
Variable shadowing hides outer variables
Closures preserve scope after function returns
Block scope in loops creates new binding per iteration
Module pattern uses scope for encapsulation

Best Practices

  1. Use const by default, let when needed
  2. Avoid var in modern JavaScript
  3. Minimize global variables to prevent pollution
  4. Use strict mode to catch accidental globals
  5. Keep scope chains short for performance
  6. Use closures for data privacy
  7. Understand lexical scope for predictable code
  8. Use block scope in loops with let
  9. Encapsulate with modules or functions
  10. Cache frequently accessed outer variables

Understanding scope is fundamental to mastering JavaScript closures, hoisting, and execution context.