When talking about the Temporal Dead Zone (TDZ), it’s impossible not to mention JavaScript’s variable hoisting feature.
In JS, variables declared with var, let, and const all undergo hoisting, but the difference is: var is initialized to undefined after hoisting and can be accessed, whereas let and const are not initialized after hoisting, and accessing them forcibly will throw an error.
At first glance this seems strange — since accessing them throws an error, it seems unnecessary for let and const to be hoisted at all, as the effect of “throwing an error” and “accessing a non‑existent variable” appears the same. But in fact, these two types of errors are different:
Uncaught ReferenceError: Cannot access '*' before initializationUncaught ReferenceError: * is not definedWhy distinguish between these two errors? Look at the example below:
js
js
If let is replaced with var, console.log will print 123, because var has no block scope, so tmp inside and outside the if block is the same variable. But let has block scope, so the if block is an independent scope, and let tmp inside the block and var tmp outside are two different variables.
If let were not hoisted, console.log would “leak” into the outer scope and read the external 123 — but that would cause confusion, because the developer declared their own tmp in this block yet reads a “someone else’s” tmp before declaring it. The purpose of hoisting is precisely this: to let the engine recognize that tmp belongs to this block scope upon entering it, thereby preventing access to an outer variable with the same name.
So why not simply report “variable undefined,” but instead create a new type of error specifically? Because from the overall context, tmp does indeed exist (there is var tmp externally), and reporting “undefined” would be misleading. The TDZ error more accurately tells you: the variable exists, but you are accessing it before it is initialized.
Finally, consider this case:
js
js
It prints 123 instead of undefined. The reason is that var has no block scope, so tmp inside and outside the if block is the same variable. Hoisting happens only once, and repeated declarations without assignment are ignored, so the existing value is naturally retained.