← All concepts
Fundamentals: Part 2

let, const, var, and the Temporal Dead Zone

let and const are block-scoped and unreachable until their line runs, while var is function-scoped and available as undefined from the top.

Loading visual…

Definition

`var` declarations are function-scoped (or global-scoped) and hoisted with an initial value of `undefined`, so a `var` variable exists and reads as `undefined` from the very top of its scope, even before its declaration line runs. This makes `var` easy to misuse: it ignores block boundaries like `if` and `for`, so the same name can leak out of a loop or conditional. `let` and `const` are block-scoped, confined to the nearest `{}`. They are registered ahead of time like `var`, but they stay in the temporal dead zone (TDZ) from the start of the block until their declaration line actually executes. Accessing a `let`/`const` variable inside its TDZ throws a `ReferenceError` instead of silently returning `undefined`. `const` additionally forbids reassigning the binding after it is set (though the value it points to, if an object, can still be mutated).

Examples

console.log(count); // ReferenceError: Cannot access 'count' before initialization
let count = 5;
console.log(count); // 5

count exists as soon as the block starts, but it stays in the temporal dead zone until its declaration line runs, so reading it earlier throws instead of returning undefined.

console.log(msg); // undefined (no error)
var msg = "hi";
console.log(msg); // "hi"

var is hoisted and initialized to undefined immediately, so reading it before its assignment line is legal but returns undefined rather than the eventual value.

for (var i = 0; i < 3; i++) {}
console.log(i); // 3, i leaked out of the loop

for (let j = 0; j < 3; j++) {}
console.log(j); // ReferenceError, j is block-scoped to the loop

var ignores the for loop's block boundary and leaks into the enclosing scope, while let is confined to the loop body.

Where you see this

Practice this

Further reading