Chapter IX · Scope

Block Scope

Block scope restricts a let or const variable's visibility to the nearest enclosing pair of curly braces {} — an if statement, a loop body, or even a standalone { } block.

01 How It Works

Think of temporary partition walls set up inside a room for the length of a meeting — once the meeting (the block) ends, the partitions come down and whatever was written on them is gone.

Block scope was introduced with let and const in ES6, specifically to fix the leaky behavior of var (which only respects function boundaries, not block boundaries). Every time control flow enters a new block — a loop iteration, an if branch — a new block scope is conceptually created for any let/const declared there. This is exactly why a for (let i ...) loop gives each iteration its own independent i, while for (var i ...) shares a single i across every iteration.

example.js
if (true) {
  let blockVal = "I am block scoped";
  console.log(blockVal); // "I am block scoped"
}
console.log(typeof blockVal); // "undefined" — it doesn't exist out here
  1. 1. Execution enters a block delimited by { }.

  2. 2. Any let or const declared directly inside that block belongs only to it.

  3. 3. When execution leaves the block, those bindings are gone — code outside can't see them.

02 Practical Example

Here is how you might see this concept applied in real-world code:

practical.js
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// Logs 0, 1, 2 — because "let" gives each loop iteration its own "i"

for (var j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 0);
}
// Logs 3, 3, 3 — because "var" shares ONE "j" across every iteration,
// and by the time the callbacks run, the loop has already finished

Key Takeaway

let and const are block-scoped, which is why let in a loop gives each iteration its own independent variable — var famously does not.

Related Concepts