JavaScript Scope

Understanding variable visibility and accessibility

🔍 What is Scope?

Scope determines where variables can be accessed in your code. Think of it like rooms in a house - some things are visible everywhere, others only in specific rooms!


let globalVar = "I'm visible everywhere!";

function myFunction() {
    let localVar = "I'm only visible inside this function!";
    console.log(globalVar);  // ✅ Can access global
    console.log(localVar);   // ✅ Can access local
}

myFunction();
console.log(globalVar);  // ✅ Can access global
// console.log(localVar);   // ❌ Error! Can't access local
                                    

Console Output:

I'm visible everywhere!
I'm only visible inside this function!
I'm visible everywhere!

Types of Scope

🌍

Global Scope

Accessible everywhere

var globalVar = "Global";
// Accessible anywhere
🏠

Function Scope

Only inside functions

function test() {
    var funcVar = "Function";
    // Only inside this function
}
🧱

Block Scope

Inside { } blocks

if (true) {
    let blockVar = "Block";
    // Only inside this block
}
🔗

Lexical Scope

Nested function access

function outer() {
    let x = 1;
    function inner() {
        console.log(x); // Can access x
    }
}

🔹 Global vs Local Scope

Understanding the difference between global and local variables:

// Global scope - accessible everywhere
let userName = "Alice";
let userAge = 25;

function displayUserInfo() {
    // Local scope - only accessible inside this function
    let greeting = "Hello";
    let message = `${greeting}, ${userName}! You are ${userAge} years old.`;
    
    console.log(message);
    console.log("Local greeting:", greeting);
}

function anotherFunction() {
    console.log("Can access global userName:", userName);
    // console.log(greeting); // ❌ Error! greeting is not accessible here
}

displayUserInfo();
anotherFunction();
console.log("Global userName:", userName);

Console Output:

Hello, Alice! You are 25 years old.
Local greeting: Hello
Can access global userName: Alice
Global userName: Alice

🔹 Block Scope with let and const

let and const have block scope, var has function scope:

function scopeDemo() {
    console.log("=== Block Scope Demo ===");
    
    if (true) {
        var varVariable = "I'm var - function scoped";
        let letVariable = "I'm let - block scoped";
        const constVariable = "I'm const - block scoped";
        
        console.log("Inside block:");
        console.log(varVariable);   // ✅ Works
        console.log(letVariable);   // ✅ Works
        console.log(constVariable); // ✅ Works
    }
    
    console.log("Outside block:");
    console.log(varVariable);   // ✅ Works - var is function scoped
    // console.log(letVariable);   // ❌ Error - let is block scoped
    // console.log(constVariable); // ❌ Error - const is block scoped
}

scopeDemo();

Console Output:

=== Block Scope Demo ===
Inside block:
I'm var - function scoped
I'm let - block scoped
I'm const - block scoped
Outside block:
I'm var - function scoped

🔹 Lexical Scope (Closures)

Inner functions can access outer function variables:

// Lexical scope example
function outerFunction(x) {
    console.log("Outer function called with:", x);
    
    // This variable is in the outer function's scope
    let outerVariable = `Outer: ${x}`;
    
    function innerFunction(y) {
        // Inner function can access outer function's variables
        let innerVariable = `Inner: ${y}`;
        
        console.log("From inner function:");
        console.log("- Can access outerVariable:", outerVariable);
        console.log("- Can access parameter x:", x);
        console.log("- Own variable:", innerVariable);
        
        return `${outerVariable} + ${innerVariable}`;
    }
    
    // Return the inner function
    return innerFunction;
}

// Create a closure
let myClosure = outerFunction("Hello");
let result = myClosure("World");
console.log("Final result:", result);

Console Output:

Outer function called with: Hello
From inner function:
- Can access outerVariable: Outer: Hello
- Can access parameter x: Hello
- Own variable: Inner: World
Final result: Outer: Hello + Inner: World

🔹 Scope Chain

JavaScript looks for variables from inner to outer scope:

// Scope chain demonstration
let level1 = "Global level";

function levelOne() {
    let level2 = "Function level";
    
    function levelTwo() {
        let level3 = "Nested function level";
        
        function levelThree() {
            let level4 = "Deep nested level";
            
            // JavaScript searches in this order:
            // 1. Current scope (level4)
            // 2. Parent scope (level3)
            // 3. Grandparent scope (level2)
            // 4. Great-grandparent scope (level1)
            
            console.log("From deepest level:");
            console.log("Level 4:", level4);  // Found in current scope
            console.log("Level 3:", level3);  // Found in parent scope
            console.log("Level 2:", level2);  // Found in grandparent scope
            console.log("Level 1:", level1);  // Found in global scope
        }
        
        levelThree();
    }
    
    levelTwo();
}

levelOne();

Console Output:

From deepest level:
Level 4: Deep nested level
Level 3: Nested function level
Level 2: Function level
Level 1: Global level

🔹 Common Scope Pitfalls

Avoid these common scope-related mistakes:

// Pitfall 1: Loop variable scope with var
console.log("=== Loop Variable Pitfall ===");

// Problem with var
for (var i = 0; i < 3; i++) {
    setTimeout(() => {
        console.log("var i:", i); // Always prints 3!
    }, 100);
}

// Solution with let
for (let j = 0; j < 3; j++) {
    setTimeout(() => {
        console.log("let j:", j); // Prints 0, 1, 2
    }, 200);
}

// Pitfall 2: Accidental global variables
function createAccidentalGlobal() {
    // Forgot to declare with let/const/var
    accidentalGlobal = "Oops, I'm global!";
}

createAccidentalGlobal();
console.log("Accidental global:", accidentalGlobal); // Works but bad practice!

Console Output:

=== Loop Variable Pitfall ===
Accidental global: Oops, I'm global!
var i: 3
var i: 3
var i: 3
let j: 0
let j: 1
let j: 2

🔹 Best Practices

Scope Best Practices:

  • Use let/const: Prefer block-scoped variables over var
  • Minimize globals: Keep global variables to a minimum
  • Declare variables: Always use let, const, or var
  • Use meaningful names: Avoid variable name conflicts
  • Keep scope small: Declare variables as close to usage as possible

🧠 Test Your Knowledge

Which keyword creates block-scoped variables?