Suggest an editImprove this articleRefine the answer for “What is temporal dead zone (TDZ) in JavaScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Temporal Dead Zone (TDZ)** is the period between entering a block scope and the line where `let` or `const` is initialized. Reading the variable in that window throws a `ReferenceError`. ```javascript console.log(x); // ReferenceError: Cannot access 'x' before initialization let x = 5; console.log(x); // 5 ``` **Key point:** `let`/`const` are hoisted but stay uninitialized. `var` initializes to `undefined` on hoist, so it has no TDZ.Shown above the full answer for quick recall.Answer (EN)Image**Temporal Dead Zone (TDZ)** is the period from the start of a block scope to the line where `let` or `const` is initialized. Accessing the variable during this period throws a `ReferenceError`. ## Theory ### TL;DR - TDZ is like a shelf marked "reserved": the slot exists in memory, but reading it throws an error. - `var` initializes to `undefined` on hoist. `let`/`const` stay uninitialized until the assignment line. - Accessing `let`/`const` before that line gives `ReferenceError`, not `undefined`. - Use `let`/`const` in all modern code. TDZ turns silent bugs into explicit errors. ### Quick example ```javascript console.log(a); // ReferenceError: Cannot access 'a' before initialization let a = 5; console.log(a); // 5 console.log(b); // undefined - no TDZ for var var b = 5; ``` Both variables are hoisted. But `var b` gets `undefined` immediately, while `let a` stays uninitialized until line 2 runs. ### Key difference `var` hoists and auto-initializes to `undefined`, so a pre-declaration read returns a value without error. `let` and `const` hoist only the binding into the lexical environment, leaving it uninitialized. Any read before the assignment line triggers a `ReferenceError`. The TDZ ends the moment the assignment executes. ### When to use - Block-scoped variable that changes value → `let`. - Value that should stay fixed → `const` (TDZ plus no reassignment). - Old `for` loop in a pre-ES6 library → `var`, no other reason. - React hooks, Express handlers, module imports → `const` by default. ### Comparison table | Variable | Hoisted | Initialized on hoist | TDZ | Pre-init access | |---|---|---|---|---| | `var` | Yes | `undefined` | No | Returns `undefined` | | `let` | Yes | No | Yes | `ReferenceError` | | `const` | Yes | No | Yes | `ReferenceError` | Classes declared with `class` follow the same rule as `let`. Accessing the class before its declaration line throws. ### How the engine handles this V8 runs two passes. The first hoists `var` declarations and sets them to `undefined`. For `let`/`const`, it creates an uninitialized binding in the lexical environment record. The second pass executes line by line. A read on an uninitialized binding throws `ReferenceError`. That is all TDZ is at the engine level. ### Common mistakes **Assuming `let` is not hoisted at all:** ```javascript let x; console.log(x); // undefined - declared but not yet assigned x = 10; console.log(x); // 10 ``` `let` is hoisted. It just stays uninitialized. That is why accessing it before the declaration line gives "cannot access before initialization", not "x is not defined". **Destructuring params without defaults:** ```javascript function fn({ a }) { console.log(a); } fn(); // ReferenceError - destructured binding has its own TDZ fn({}); // undefined - fine ``` Fix: `function fn({ a = 1 })`. **Confusing `ReferenceError` with `TypeError`:** ```javascript const x = 1; x = 2; // TypeError, not ReferenceError ``` `ReferenceError` happens during TDZ, before init. `TypeError` happens after TDZ ends, when you try to reassign a `const`. Different errors, different causes. Most TDZ bugs I've seen in code review come from refactoring `var` to `let` without checking whether the variable is read before its declaration in the same block. ### Real-world usage - React: `const [state, setState] = useState(null)` - TDZ prevents reads before the hook line. - Express: `const id = req.params.id` inside a route handler - catches accidental pre-parse access. - Node.js modules: `const fs = require('fs')` at top level - strict mode enforces TDZ throughout. - Redux: `const action = { type: 'FETCH_USER' }` - prevents `undefined` action types from reaching reducers. ### Follow-up questions **Q:** What is the output of `console.log(x); let x = 5;`? **A:** `ReferenceError: Cannot access 'x' before initialization`. The variable is hoisted but stays in TDZ until the assignment runs. **Q:** Does `const` have a TDZ? **A:** Yes, same rules as `let`. TDZ ends at the assignment line. After that, trying to reassign throws `TypeError`, not `ReferenceError`. **Q:** Why does TDZ exist? **A:** It makes early variable access a loud, explicit error instead of returning `undefined` without error. With `var`, pre-declaration reads return `undefined` with no warning, which can cause bugs that are hard to trace. **Q:** How does Babel simulate TDZ when transpiling to ES5? **A:** Babel uses a `void 0` sentinel check to approximate the uninitialized state. It gets close to the spec behavior, but edge cases can differ from native engine handling. ## Examples ### Basic TDZ behavior ```javascript // Both variables are hoisted from the start of the scope. // var: initialized to undefined immediately. // let: stays uninitialized until the declaration line runs. console.log(name); // undefined (var - no TDZ) var name = 'Alice'; console.log(age); // ReferenceError: Cannot access 'age' before initialization let age = 30; ``` `name` returns `undefined` because `var` initializes on hoist. `age` throws because `let` leaves its binding uninitialized until line 9. ### TDZ in a request handler ```javascript // Safe pattern - access happens after declaration function handleLogin(userId) { if (!userId) throw new Error('Missing user ID'); const token = generateToken(userId); // TDZ has passed by this line return token; } // Unsafe pattern - TDZ violation function badHandler(userId) { console.log(token); // ReferenceError - token is in TDZ here const token = generateToken(userId); return token; } ``` In `handleLogin`, `token` is only read after its declaration. In `badHandler`, reading `token` before the `const` line throws immediately. TDZ surfaces this in development, not in production.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.