Find reads before initialization
Debugging an early ReferenceError
JS Puzzles · Const
The temporal dead zone is the interval after a lexical binding's scope starts but before execution initializes its let, const, or class declaration. Reading the binding during that interval throws a ReferenceError.
A let, const, or class binding exists from the beginning of its block, but it is uninitialized until execution reaches its declaration. Reading it during that interval throws a ReferenceError; the relevant boundary is execution order, not merely where the declaration appears in the source.
let, const, and class, the TDZ begins at block entry and ends when execution reaches the initialized declaration.let declaration without an initializer becomes undefined once execution reaches it.undefined.{
try {
console.log(value);
} catch (error) {
console.log(error instanceof ReferenceError);
}
const value = 42;
console.log(value); // 42
}The first access runs before initialization and throws; after the declaration executes, the binding can be read normally.
Debugging an early ReferenceError
Reviewing shadowed variables
Designing initialization order
A TDZ is the interval from the start of a block until execution reaches and initializes a let, const, or class declaration. Accessing the binding in that interval throws a ReferenceError.
Follow-up: Does source position alone determine the error? No; the ordering of execution and the declaration's initialization is what matters.
var?let and const bindings are available only after execution reaches their declaration, so an earlier read fails. A var binding instead receives undefined when hoisted, so this particular TDZ error does not occur.
Follow-up: What about typeof? It still throws for a lexical binding inside its TDZ.
The binding is initialized when execution reaches the declaration. With let and no initializer, that initialization gives the variable undefined; with const, an initializer is required.
Follow-up: Why must const have an initializer? It cannot be assigned later, so leaving it uninitialized would make it unusable.
let declarationsconst declarationsclass declarationsReferenceErrorblock scopevariable hoisting