Block Scope
Introduction
A block is a pair of curly braces: an if body, a for body, a while body, or even a standalone { ... }. Variables declared with let and const are scoped to the nearest block.
Block scope is one of the biggest practical improvements in modern JavaScript because it makes variables live exactly where readers expect them to live.
Why This Matters
Many classic interview bugs come from using var in loops and callbacks. let fixes those bugs not by magic, but by creating a fresh binding for each block or loop iteration. This is a must-know bridge from basic scope to closures.
Theory
let and const are block-scoped
A let or const binding exists only inside the closest { ... } block that contains it. Once execution leaves the block, that name is not available.
const means the binding cannot be reassigned
const prevents reassignment of the variable binding, not mutation of the value. A const object can still have properties changed; the variable just cannot be pointed at a different object.
The temporal dead zone
let and const are hoisted in a technical sense, but they are not usable before the declaration line runs. The region before the declaration is called the temporal dead zone (TDZ), and reading the variable there throws a ReferenceError.
Loop bindings
for (let i = 0; ...) creates a new i binding for each iteration. That is why callbacks created in a let loop remember the value from their own iteration. for (var i = 0; ...) uses one function-scoped i, so every callback shares the same final value.
| Pattern | Binding behavior | Callback result |
|---|---|---|
for (var i...) | one shared function-scoped i | all callbacks see final i |
for (let i...) | fresh i per iteration | each callback sees its own i |
IIFE around var | captures argument per call | each callback sees captured copy |
Visual Diagrams
function demo() scope
var a ----------------------------------- visible through whole function
if (condition) {
let b -------- visible only inside this block
const c ------- visible only inside this block
}
b and c are gone here
a is still visible here`let` and `const` stop at the block boundary; `var` stops at the function boundary.
Code Examples
`if` blocks hide `let` and `const`
The status binding exists only inside the if block. Use this to reduce accidental reuse.
The loop closure bug and both fixes
The var version shares one binding. The let version creates per-iteration bindings. The IIFE version captures a value in a function argument.
Output Prediction
Predict the output #1
1const varCallbacks = [];2for (var i = 0; i < 3; i++) {3 varCallbacks.push(function () {4 return i;5 });6}7 8const letCallbacks = [];9for (let j = 0; j < 3; j++) {10 letCallbacks.push(function () {11 return j;12 });13}14 15console.log(varCallbacks[0](), varCallbacks[1](), varCallbacks[2]());16console.log(letCallbacks[0](), letCallbacks[1](), letCallbacks[2]());Interview Questions
1Why does `let` fix the classic loop callback problem?
A for loop with let creates a new binding for the loop variable on each iteration. Each callback closes over its own iteration's binding. With var, there is one function-scoped binding shared by all callbacks, so they all see the final value after the loop finishes.
Follow-ups
- How would you fix it before `let` existed?
- What role does an IIFE play here?
2What is the difference between `const` and immutability?
const makes the variable binding non-reassignable. It does not freeze the value. A const object can still be mutated unless you also use techniques such as Object.freeze or immutable data structures.
Quiz
1. Why do callbacks created in `for (let i = 0; i < 3; i++)` see `0`, `1`, and `2` instead of all seeing `3`?
2. What happens if you read a `let` variable before its declaration line runs?
Summary
- Blocks are `{ ... }` regions; `let` and `const` are scoped to the nearest block.
- `var` ignores block scope and belongs to the nearest function or global scope.
- `for (let ...)` creates a fresh binding per iteration, fixing the classic callback bug.
- `const` prevents reassignment of the binding, not mutation of the value.