JavaScript
easy
very high
junior
Difference between `var`, `let`, and `const`
`var` is function-scoped and hoisted to `undefined`. `let`/`const` are block-scoped with a temporal dead zone. `const` forbids reassignment but the value can still be mutated.
5 min read·~8 min to think through
Three axes to compare:
var | let | const | |
|---|---|---|---|
| Scope | function | block | block |
| Hoisting | hoisted, initialized to undefined | hoisted, but in TDZ until declaration | same as let |
| Reassign | yes | yes | no |
| Redeclare in same scope | yes (silently) | no (SyntaxError) | no |
The Temporal Dead Zone (TDZ) is the window between entering a block and the actual let/const line — accessing the binding throws ReferenceError. This catches typos that var would silently let through.
const blocks reassignment of the binding, not mutation of the value. const arr = []; arr.push(1) is fine; const arr = []; arr = [1] is not.
Default to const. Use let only when you actually reassign. Reach for var essentially never in modern code — the only legitimate case is hot-patching legacy scripts that rely on its hoisting.
Code
Follow-up questions
- •What is the temporal dead zone, and why does it exist?
- •How does hoisting differ between function declarations and var?
- •Why does using let in a for loop fix the classic closure-in-loop bug?
Common mistakes
- •Thinking `const` makes the value immutable — it doesn't.
- •Using `var` in a `for` loop and being surprised that callbacks all see the final value.
- •Believing TDZ means the variable isn't hoisted — it is, but accessing it throws.
Performance considerations
- •TDZ checks have negligible runtime cost; modern engines elide them after the first read.
- •Block scoping enables tighter escape analysis and smaller closure environments — generally a win.
Edge cases
- •`typeof` on a TDZ binding still throws (unlike on a truly undeclared identifier, where it returns 'undefined').
- •`var` declarations at the top of a module bind to the module scope, not the global object — unlike scripts.
Real-world examples
- •ESLint's `prefer-const` codifies the 'default to const' rule across most modern codebases.
- •TypeScript narrowing works better with `const` because the binding can't change between checks.
Senior engineer discussion
At senior level you should be able to talk about how V8 represents these in its environment records, why `let` in a for-loop creates a per-iteration binding (a spec choice for closure correctness), and how this interacts with `for..in` / `for..of` iteration order.
Related questions
JavaScript
Medium
hot
6 min