Scope & Hoisting
Scope is the set of rules by which the engine decides where a name is available and which variable it points at. JavaScript has three scope levels — global, function, and block — and they behave differently for var versus let/const. On top of that runs the lexical model: a name's scope is fixed by where the code is written, not where it is called — which is exactly what closures rest on.
The second pillar is hoisting: the engine registers declarations before running the code, in the creation phase of the execution context. Every "oddity" grows from here: var reads as undefined before its line, a function can be called above its declaration, and let/const fall into a temporal dead zone. Each layer below dissects one part of this machine.
Topic map
- Global scope — the top-level scope and how to create a global variable accidentally by assigning without a keyword.
- Function scope — why
varis visible throughout the function regardless of nested blocks. - Block scope — how
letandconstare bounded by the nearest{}whilevarignores them. - Lexical scope — how the scope chain resolves a name outward and why scope is fixed where the code is written.
- var, let, const — a summary table of the differences by scope, hoisting, reassignment, and redeclaration.
- Hoisting — registering declarations before execution and the difference between hoisting
var, functions, andlet/const. - Temporal dead zone (TDZ) — the span where a
let/constname already exists but reading it throws aReferenceError. - Variable shadowing — how an inner name hides an outer one and what illegal shadowing is.
- Execution context — the creation and execution phases where hoisting, the TDZ, the scope chain, and
thisare born.
Common traps
| Mistake | Consequence |
|---|---|
Assigning without var/let/const | An accidental global variable (or ReferenceError in strict mode) |
Treating var as block-scoped | It is function-scoped — it leaks out of if/for into the whole function |
Reading let/const before their line | ReferenceError from the TDZ, not undefined |
Expecting a ReferenceError before a var's line | No — var is already undefined (hoisted and initialized) |
| Confusing an undeclared and an undefined variable | The first is a ReferenceError, the second is the value undefined |
A var beside a same-named let in one scope | A SyntaxError of illegal shadowing |
Interview relevance
Scope and hoisting are a favorite way to test whether you understand the execution model, not just the syntax. The classic question is "what does reading a variable before its declaration print": the right answer depends on the keyword (undefined for var, ReferenceError for let) and requires explaining the creation-phase mechanism.
Typical checks:
- The three scopes and which declaration respects which.
- What hoisting is and how hoisting
var, a function, andlet/constdiffer. - What the TDZ is and why
letbefore its line is an error, notundefined. - The lexical model and the scope chain as the foundation of closures.
Common wrong answer: "hoisting physically moves the code to the top". In fact the code moves nowhere — the engine merely registers declarations in the context creation phase, binding var to undefined and leaving let/const uninitialized until their line.