# Temporal Dead Zone in JavaScript: Understanding let and const Hoisting

> Understand the Temporal Dead Zone TDZ in JavaScript with let and const. Learn how uninitialized variables cause ReferenceError and affect hoisting in this concise explanation.

- Repository: [Kyle Simpson/You-Dont-Know-JS](https://github.com/getify/You-Dont-Know-JS)
- Tags: deep-dive
- Published: 2026-02-24

---

**The Temporal Dead Zone (TDZ) is the runtime period between when a block-scoped variable is hoisted to the top of its lexical scope and when its declaration statement executes, during which the variable exists in an uninitialized state and any access attempt throws a ReferenceError.**

The Temporal Dead Zone represents a fundamental shift in how JavaScript handles variable declarations, introducing strict temporal constraints that prevent early access to `let` and `const` bindings. As documented in the **getify/You-Dont-Know-JS** repository, this behavior differs sharply from the loose initialization rules of `var` declarations. Understanding TDZ mechanics helps developers avoid runtime errors when using modern block-scoped variables.

## What Is the Temporal Dead Zone?

In [`scope-closures/ch5.md`](https://github.com/getify/You-Dont-Know-JS/blob/main/scope-closures/ch5.md), the book explains that while `let` and `const` declarations are hoisted—meaning their identifiers are registered at the top of their containing scope—they do not receive automatic initialization. Instead, the compiler inserts an auto-initialization instruction at the exact line where the declaration appears in source code (lines 29-33).

The TDZ spans the temporal gap between scope entry and the execution of that initialization instruction. During this window, the variable identifier exists in the lexical environment but remains in an **"uninitialized"** state distinct from `undefined`. Any attempt to read or reference the variable during this period results in a `ReferenceError`.

### How var Differs from let and const

According to [`scope-closures/ch5.md`](https://github.com/getify/You-Dont-Know-JS/blob/main/scope-closures/ch5.md) (lines 33-34), `var` declarations technically also have a TDZ, but it has zero length because the engine initializes them to `undefined` immediately upon entering the scope. This makes the TDZ unobservable for `var` variables, whereas `let` and `const` purposefully defer initialization to prevent accidental use of uninitialized values.

## How TDZ Affects let and const Declarations

The practical impact of TDZ manifests as runtime errors when code attempts to access block-scoped variables too early. The [`scope-closures/ch1.md`](https://github.com/getify/You-Dont-Know-JS/blob/main/scope-closures/ch1.md) file illustrates this through concrete examples where execution order creates unexpected reference errors.

### Basic TDZ Error with let

Accessing a `let` variable before its declaration line executes throws an immediate error:

```javascript
function demo() {
  console.log(message); // ReferenceError: Cannot access 'message' before initialization
  let message = "Hello, TDZ!";
}
demo();

```

The identifier `message` is hoisted to the top of `demo()`, but remains uninitialized until the `let message` line runs, creating a TDZ that encompasses the `console.log` call.

### TDZ Behavior with const

`const` declarations follow identical TDZ rules to `let`, with the additional constraint that initialization must occur at declaration time:

```javascript
{
  console.log(value); // ReferenceError: Cannot access 'value' before initialization
  const value = 42;
}

```

### Temporal vs. Positional Ordering

As noted in [`scope-closures/ch5.md`](https://github.com/getify/You-Dont-Know-JS/blob/main/scope-closures/ch5.md) (lines 35-48), the TDZ is a time-based rather than position-based restriction. A function called before a `let` declaration—even if the call appears after the declaration in the source file—will encounter the variable in its TDZ if the call executes first.

### Nested Scope Shadowing

TDZ errors can occur even when outer scope variables exist with the same name. Inner declarations shadow outer variables immediately upon scope entry, not at the declaration line:

```javascript
let outer = "outside";

{
  console.log(outer); // ReferenceError: Cannot access 'outer' before initialization
  let outer = "inside";
}

```

The inner `let outer` declaration hoists and creates its own TDZ immediately upon entering the block, preventing access to the outer `outer` variable throughout the entire block until the declaration executes.

## Best Practices to Avoid TDZ Errors

The [`scope-closures/ch5.md`](https://github.com/getify/You-Dont-Know-JS/blob/main/scope-closures/ch5.md) file (lines 78-81) recommends declaring all `let` and `const` variables **at the top of their scope**—whether function, block, or module—to minimize or eliminate the TDZ window. This practice reduces the risk of accidental early access and makes code intent clearer.

Additionally, be aware that complex nested scopes can create overlapping TDZs where inner variables shadow outer ones immediately, not just after their declaration line.

## Summary

- The Temporal Dead Zone is the interval between scope entry and declaration execution where `let`/`const` identifiers exist but remain uninitialized.
- Accessing variables during their TDZ throws a `ReferenceError` rather than returning `undefined`.
- `var` declarations have a zero-length TDZ because they auto-initialize to `undefined` immediately.
- Declaring block-scoped variables at the top of their scope eliminates TDZ risks.
- Inner scope declarations create immediate TDZs that shadow outer variables, not just at the declaration line.

## Frequently Asked Questions

### What error does the Temporal Dead Zone throw?

The TDZ throws a `ReferenceError` with a message indicating the variable cannot be accessed before initialization. This differs from `var` hoisting, which returns `undefined` for early accesses, making TDZ errors explicit and easier to debug according to [`scope-closures/ch1.md`](https://github.com/getify/You-Dont-Know-JS/blob/main/scope-closures/ch1.md).

### Does var have a Temporal Dead Zone?

Technically yes, but it has zero length. According to [`scope-closures/ch5.md`](https://github.com/getify/You-Dont-Know-JS/blob/main/scope-closures/ch5.md), `var` declarations are initialized to `undefined` immediately upon scope entry, making their TDZ unobservable. Only `let` and `const` have observable TDZs because they defer initialization until the declaration line executes.

### Why does accessing a let variable before declaration throw an error instead of returning undefined?

The ECMAScript specification designed `let` and `const` to catch programming errors early. By throwing a `ReferenceError` rather than returning `undefined`, JavaScript prevents developers from accidentally relying on hoisted variables before their intended initialization, as summarized in [`get-started/ch4.md`](https://github.com/getify/You-Dont-Know-JS/blob/main/get-started/ch4.md).

### How can I avoid Temporal Dead Zone errors in my code?

Declare all `let` and `const` variables at the top of their containing scope (function, block, or module) to shrink the TDZ to zero length. This ensures the initialization instruction executes before any code that references the variable, eliminating the risk of early access errors.