JavaScript Scope & The Scope Chain

Understand where variables live, why curly braces lock in data with let and const, and how JavaScript finds variables.

1. What is Scope?

Scope simply answers one question: "Where in my code can I actually use this variable?"

💡 Think of rooms in a house

If you put a chair inside your bedroom (a function or {} block), people walking on the street outside cannot see or use your chair. But if something is placed in the open front yard (Global Scope), everyone in the entire house can see and use it.

Function Scope

Any variable created inside a function stays trapped inside that function.

function login() {
    const token = "SECRET_123";
}
// console.log(token); 
// ❌ Error: token is trapped inside login()

Block Scope (`let` & `const` ✅)

Any variable created with let or const stays locked inside curly braces {} (like if or for).

if (true) {
    let role = "Admin";
}
// console.log(role);
// ❌ Error: role is trapped inside {}

2. Why `var` is Dangerous (It Ignores Curly Braces)

In old JavaScript, var did not care about if blocks or for loops. It would sneak out and pollute the rest of your code:

⚠️ The var Leakage Problem

If you create var score = 100 inside an if block, it escapes into the whole file. Always use let and const in modern JavaScript to keep your variables safely contained inside their curly braces.

// ❌ Old var leaks out of {} blocks:
if (true) {
    var leaked = "I escaped!";
}
console.log(leaked); // "I escaped!" (Pollutes outer code!)

// ✅ Modern let stays safely trapped:
if (true) {
    let safe = "I am locked in!";
}
// console.log(safe); // ❌ Error: safe is not defined
Output
I escaped!

3. The Scope Chain (How JavaScript Finds Variables)

When JavaScript needs to read a variable, it looks inside your code like someone searching for their keys:

  1. Step 1: It checks the current room (local scope). If found, it uses it!
  2. Step 2: If not found, it steps outside into the parent room (enclosing function).
  3. Step 3: If still not found, it checks the whole house (global scope).
  4. Step 4: If it still can't find it anywhere, it stops and gives you a ReferenceError!
const globalName = "Earth";

function country() {
    const countryName = "India";

    function city() {
        const cityName = "Mumbai";
        // The inner function can see everything outside it:
        console.log(`${cityName} is in ${countryName}, on planet ${globalName}`);
    }

    city();
}

country();
Output
Mumbai is in India, on planet Earth

4. Variable Shadowing (Hiding an Outer Variable)

If an inner block creates a variable with the exact same name as an outer variable, the inner variable temporarily hides (shadows) the outer one inside that block:

const theme = "dark"; // Outer global theme

function setCustomTheme() {
    const theme = "light"; // Shadows the outer "dark" theme inside this function!
    console.log("Inside function theme is:", theme); // "light"
}

setCustomTheme();
console.log("Outside in global theme is:", theme);    // "dark" (Untouched!)
Output
Inside function theme is: light
Outside in global theme is: dark

5. Strict Mode: Preventing Accidental Global Variables

If you mistype a variable name without let or const (like myScore = 100), old JavaScript would accidentally create a global variable! Writing "use strict" at the top prevents this by throwing an immediate error:

function testSafety() {
    "use strict";
    try {
        // Mistyped without let or const
        score = 99; 
    } catch (err) {
        console.log("Strict mode caught the typo:", err.message);
    }
}

testSafety();
Output
Strict mode caught the typo: score is not defined

6. Quick Scope Summary

Scope Where it lives Who can access it?
Global Scope Outside all functions and blocks Accessible by any code anywhere in the file
Function Scope Inside a function() {} Only accessible inside that specific function
Block Scope Inside curly braces {} with let or const Only accessible inside those curly braces

7. Scope Interview Deck

Test your knowledge on block scope traps, lexical scope lookup, shadowing, TDZ in functions, and strict mode:

🎯 Scope Interview Deck
Question 1 of 5
⭐ Easy • Block vs Function Scope var vs let
if (true) {
    var a = 10;
    let b = 20;
}

console.log(a);
console.log(b);
What will happen when this code runs?
A) Logs 10, then throws ReferenceError: b is not defined
B) Logs 10, then logs 20
C) Logs undefined, then undefined
D) Throws ReferenceError: a is not defined
⭐ Easy • Scope Chain Lookup Scope Chain
let count = 0;

function outer() {
    let count = 10;
    function inner() {
        console.log(count);
    }
    inner();
}

outer();
What will be logged to the console?
A) 10
B) 0
C) undefined
D) ReferenceError
⚡ Medium • Variable Shadowing Shadowing
let x = "global";

function test() {
    let x = "local";
    if (true) {
        let x = "block";
    }
    console.log(x);
}

test();
What will be printed to the console?
A) "local"
B) "block"
C) "global"
D) undefined
🔥 Hard • TDZ with Shadowing TDZ & Hoisting
let val = 50;

function compute() {
    console.log(val);
    let val = 100;
}

compute();
What is the result of running this code?
A) ReferenceError: Cannot access 'val' before initialization
B) Logs 50
C) Logs undefined
D) Logs 100
⭐ Easy • Strict Mode Globals Strict Mode
function leak() {
    "use strict";
    hidden = 42;
}

leak();
What happens when leak() is invoked?
A) Throws ReferenceError: hidden is not defined
B) Creates window.hidden = 42
C) Returns undefined without errors
D) Throws SyntaxError

8. Practice Challenge

🎯 Coding Challenge: 3-Level Scope Hierarchy

Build a 3-level scope chain:
1. A global constant appName = "DevHub".
2. A function-scoped constant version = "1.0.0" inside initializeApp().
3. A block-scoped variable build = "Release-92" inside an if (true) block.
Print all 3 values together from inside the deepest block.

Your output will appear here.