Block Scope with let & const0%

Block Scope with let & const

Beginner12 min read•Updated: Jul 11, 2026
Study Materials

Block Scope with let and const

In early JavaScript (ES5 and earlier), variable scoping was exclusively determined by functions. With the arrival of ECMAScript 2015 (ES6), block scoping was introduced via let and const. Understanding how block scopes, lexical environments, and the Temporal Dead Zone (TDZ) function under the hood is fundamental to intermediate JavaScript mastery.


1. Scoping Evolution: Function Scope vs. Block Scope

A block in JavaScript is defined as any pair of matching curly braces { ... }:

  • Inside if / else statements
  • Inside for, while, and do...while loops
  • Inside standalone blocks { ... }
JavaScript
// Function Scope (Legacy var):
function testVar() {
if (true) {
var greeting = "Hello from var";
}
console.log(greeting); // "Hello from var" - Leaked outside the if block!
}
 
// Block Scope (Modern let & const):
function testLetConst() {
if (true) {
let message = "Hello from let";
const pi = 3.14159;
}
console.log(message); // ReferenceError: message is not defined!
console.log(pi); // ReferenceError: pi is not defined!
}

2. The Classic Closure in Loops Problem

Consider this classic frontend interview challenge:

JavaScript
// ❌ BROKEN WITH VAR:
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 100);
}
// Outputs: 3, 3, 3! (Because var i is shared across the entire function scope!)

Why let Solves This Elegantly:

In JavaScript engines, let inside a for loop header creates a brand new lexical binding for every single iteration:

JavaScript
// ✅ FIXED WITH LET:
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 100);
}
// Outputs: 0, 1, 2! (Each timeout closure captures its own private block-scoped `i`!)

3. The Temporal Dead Zone (TDZ) Deep Dive

Many developers mistakenly believe that let and const are not hoisted. They ARE hoisted, but unlike var (which is initialized with undefined), let and const remain uninitialized in the Temporal Dead Zone (TDZ):

JavaScript
// Proof of TDZ hoisting:
let value = "outer";
 
function demonstrateTDZ() {
// If 'let value' was not hoisted, this would log "outer".
// Because it IS hoisted to this block scope, accessing it before initialization throws!
console.log(value); // ReferenceError: Cannot access 'value' before initialization!
let value = "inner";
}
 
demonstrateTDZ();

4. Shadowing Variables

Variable shadowing occurs when a variable declared within an inner block scope has the same identifier as a variable in an outer scope:

JavaScript
const userRole = "viewer"; // Outer scope
 
function checkAccess() {
if (true) {
const userRole = "admin"; // Shadows outer userRole inside this block!
console.log(`Inner role: ${userRole}`); // "admin"
}
console.log(`Outer role: ${userRole}`); // "viewer"
}
 
checkAccess();

Interactive Quiz

Practice Quiz

Question 1
What creates a new block scope in modern JavaScript?
Question 2
Why does for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 10); } print 3, 3, 3?
Question 3
What happens when accessing a let variable during its Temporal Dead Zone (TDZ)?
Question 4
Can a let variable be re-declared within the exact same block scope?
Question 5
What is "variable shadowing"?
Next Lesson

Template Literals

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Related Lessons

Previous LessonNext Lesson
NoneTemplate Literals

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext