Understand block boundaries and why accessing let/const before declaration throws a ReferenceError in the TDZ.
Learn how variables act as labeled containers for data using const, let, and var to manage lexical scope and reassignment safely in modern applications.
A block is defined by a pair of curly braces {} (such as inside if statements, for loops, or standalone blocks).
let and const are block-scoped. Unlike legacy var (which leaks outside blocks), let and const variables die when their enclosing block finishes executing.
The TDZ is the time window between when a let or const variable enters scope (Phase 1 Memory Allocation) and when it is declared and assigned a value (Phase 2 Execution). Accessing a variable while it is in its TDZ throws an uncaught ReferenceError!
if (true) {
// === TDZ for 'score' starts here! ===
// console.log(score); // ReferenceError: Cannot access 'score' before initialization
let score = 95; // === TDZ ends here! ===
console.log(score); // 95 (Safe to access)
}
function testBlock() {
if (true) {
var varVariable = "I leak out!";
let letVariable = "I stay inside!";
}
console.log(varVariable); // "I leak out!" (var ignores block boundary!)
// console.log(letVariable); // ReferenceError! (let is block-scoped)
}
let variable inside a block with the same name as an outer variable shadows (overrides) the outer variable inside that block.var in For Loops: Using var i = 0 in loops creates a single global/function loop variable shared across async iterations. Use let i = 0 so each loop iteration gets its own fresh block variable!Always use let and const to benefit from block scoping and TDZ safety. Next, let's learn JavaScript's superpower: Closures!