Scope and Scope Chain
Scope and Scope Chain
How JavaScript decides which variables are accessible and resolves identifiers across lexical boundaries.
The Multi-Tier Glass Skyscraper
JavaScript uses Lexical Scoping: variable accessibility is strictly determined by where variables and blocks are authored in your source code, not where functions are invoked at runtime.
Global Scope
Variables declared outside any function or block live here. They are accessible throughout the entire script and remain in memory for the life of the document.
Function (Local) Scope
Variables declared with `var`, `let`, or `const` inside a function body are private to that function and cannot be read from the outside.
Block Scope (ES6)
Any pair of curly braces `{ ... }` creates a block scope for `let` and `const`. Variables declared with `var` ignore block boundaries and bleed out into the enclosing function or global scope.
The Scope Chain Lookup Mechanism
When resolving an identifier, the engine searches the current local scope first. If missing, it steps outward to the parent lexical environment, repeating upward until the Global Scope. If still unresolved, it throws a `ReferenceError`.
Observing the outward-only traversal of the Scope Chain:
const globalCity = "New York";
function travelAgency() {
const localCurrency = "USD";
function bookFlight() {
const passenger = "Alice";
// Accessible: passenger (local), localCurrency (parent), globalCity (global)
console.log(`${passenger} pays ${localCurrency} in ${globalCity}`);
}
bookFlight();
// console.log(passenger); // Throws ReferenceError: passenger is not defined
}
travelAgency();
Alice pays USD in New York
Accidental Global Leaks
The Risk: Assigning to an undeclared variable (e.g., `count = 10` without `let/const/var`) in non-strict mode automatically creates a property on the global window object.
The Fix: Always use `"use strict";` or modern ES modules (which are strict by default) and declare all variables with `const` or `let`.
Var Bleeding Past Blocks
The Risk: Using `var` inside `if` blocks or `for` loops leaks variables to the surrounding function, causing unintended state collisions.
The Fix: Default to `const`, and use `let` only when reassigning loop counters or mutable state. Never use `var` in modern JS.
- ✓ Lexical Scope: Determined at author time by physical code structure, not call location.
- ✓ Scope Chain: Searches current local → parent lexical → ... → global scope (one-way only).
- ✓ `let` and `const` are block-scoped; `var` is function-scoped.
- ✓ Unresolved variable read throws `ReferenceError`.
- ✓ Modules and strict mode prevent accidental global variable creation.