Why do let and const throw before declaration?
By FrontendPro Editorial Team Updated 9/26/2026
Answer
All three are hoisted — the engine registers the name before any code runs. The difference is whether the binding is initialised at that moment.
var is registered and set to undefined, so reading it early is legal and silently gives you nothing useful. let and const are registered but deliberately left uninitialised. The gap between the top of the scope and the declaration line is the temporal dead zone, and touching the name inside it throws:
console.log(a); // undefined
var a = 1;
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 2;
Note the error message. It is not "b is not defined" — the engine knows b exists, which is precisely how it can refuse the access.
Why this is a feature
The var behaviour hides mistakes. A typo or a misordered statement produces undefined, the program continues, and you discover the problem several lines later where the symptom makes no sense. The TDZ converts that into an immediate error at the exact line.
It also makes const coherent. A const must be assigned at declaration, so there is no valid moment when reading it before that line could return anything meaningful — throwing is the only honest answer.
Two details people miss
The TDZ is about time, not position. A function defined above the declaration can still read the variable, as long as it is called afterwards:
const show = () => console.log(x); // fine to define
let x = 5;
show(); // 5 — called after initialisation
And typeof is not a safe guard any more. It returns "undefined" for names that were never declared, but throws inside a TDZ:
typeof neverDeclared; // "undefined"
typeof y; // ReferenceError
let y = 1;
