Back to 33 Js Concepts

Temporal Dead Zone in JS

docs/beyond/concepts/temporal-dead-zone.mdx

latest34.6 KB
Original Source

Why does this code throw an error?

javascript
console.log(name)  // ReferenceError: Cannot access 'name' before initialization
let name = "Alice"

But this code works fine?

javascript
console.log(name)  // undefined (no error!)
var name = "Alice"

The difference is the Temporal Dead Zone (TDZ). It's a behavior that makes let, const, and class declarations safer than var by catching bugs early.

<Info> **What you'll learn in this guide:** - What the Temporal Dead Zone is and why it exists - How TDZ affects `let`, `const`, `class`, and default parameters - Why it's called "temporal" (hint: it's about time, not position) - The key differences between TDZ and `var` hoisting - How `typeof` behaves differently in the TDZ - TDZ edge cases in destructuring, loops, and ES modules - Common TDZ pitfalls and how to avoid them </Info> <Warning> **Prerequisite:** This guide assumes you understand [scope and closures](/concepts/scope-and-closures). You should know the difference between global, function, and block scope before diving into TDZ. </Warning>

What is the Temporal Dead Zone?

The Temporal Dead Zone (TDZ) in JavaScript is the period between entering a scope and the line where a let, const, or class variable is initialized. During this zone, the variable exists but cannot be accessed—any attempt throws a ReferenceError. The TDZ prevents bugs by catching accidental use of uninitialized variables. As defined in the ECMAScript specification, let and const bindings are created when their containing environment record is instantiated but remain uninitialized until their declaration is evaluated.

javascript
{
  // TDZ for 'x' starts here (beginning of block)
  
  console.log(x)  // ReferenceError: Cannot access 'x' before initialization
  
  let x = 10      // TDZ for 'x' ends here
  
  console.log(x)  // 10 (works fine)
}

The TDZ applies to:

  • let declarations
  • const declarations
  • class declarations
  • Function default parameters (in certain cases)
  • Static class fields

The Restaurant Reservation Analogy

Think of the TDZ like a restaurant reservation system.

When you make a reservation, your table is reserved from the moment you call. The table exists, it has your name on it, but you can't sit there yet. If you show up early and try to sit down, the host will stop you: "Sorry, your table isn't ready."

The table becomes available only when your reservation time arrives. Then you can sit, order, and enjoy your meal.

┌─────────────────────────────────────────────────────────────────────────┐
│                     THE TEMPORAL DEAD ZONE                               │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   {  // You enter the restaurant (scope begins)                          │
│                                                                          │
│     ┌──────────────────────────────────────────────┐                     │
│     │         TEMPORAL DEAD ZONE FOR 'x'           │                     │
│     │                                              │                     │
│     │   Table reserved, but NOT ready yet          │                     │
│     │                                              │                     │
│     │   console.log(x);  // "Table isn't ready!"   │                     │
│     │                    // ReferenceError         │                     │
│     │                                              │                     │
│     └──────────────────────────────────────────────┘                     │
│                                                                          │
│     let x = 10;  // Reservation time! Table is ready.                    │
│                                                                          │
│     console.log(x);  // "Here's your table!" → 10                        │
│                                                                          │
│   }                                                                      │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

Variables in the TDZ are like reserved tables: they exist, JavaScript knows about them, but they're not ready for use yet.


TDZ vs var Hoisting

Both var and let/const are hoisted, meaning JavaScript knows about them before code execution. The difference is in initialization:

Aspectvarlet / const
Hoisted?YesYes
Initialized at hoisting?Yes, to undefinedNo (remains uninitialized)
Access before declaration?Returns undefinedThrows ReferenceError
Has TDZ?NoYes
┌─────────────────────────────────────────────────────────────────────────┐
│                      var vs let/const HOISTING                           │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   VAR: Hoisted + Initialized             LET/CONST: Hoisted Only         │
│   ──────────────────────────             ────────────────────────        │
│                                                                          │
│   ┌─────────────────────┐               ┌─────────────────────┐          │
│   │ // JS does this:    │               │ // JS does this:    │          │
│   │ var x = undefined   │ ← ready       │ let y (uninitialized)│ ← TDZ   │
│   └─────────────────────┘               └─────────────────────┘          │
│           │                                      │                       │
│           ▼                                      ▼                       │
│   console.log(x) // undefined           console.log(y) // Error!         │
│           │                                      │                       │
│           ▼                                      ▼                       │
│   var x = 10  // reassignment           let y = 10  // initialization    │
│           │                                      │                       │
│           ▼                                      ▼                       │
│   console.log(x) // 10                  console.log(y) // 10             │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

Here's the behavior in code:

javascript
function varExample() {
  console.log(x)  // undefined (not an error!)
  var x = 10
  console.log(x)  // 10
}

function letExample() {
  console.log(y)  // ReferenceError: Cannot access 'y' before initialization
  let y = 10
  console.log(y)  // never reaches here
}

Why "Temporal"?

The word "temporal" means related to time. The TDZ is "temporal" because it depends on when code executes, not where it appears in the source. MDN documents this distinction explicitly: the zone is defined by the execution flow, not the lexical position of the code.

This is a subtle but important distinction. Look at this example:

javascript
{
  // TDZ for 'x' starts here
  
  const getX = () => x  // This function references x
  
  let x = 42            // TDZ ends here
  
  console.log(getX())   // 42 - works!
}

Wait, the function getX is defined before x is initialized. Why doesn't it throw an error?

Because the TDZ is about execution time, not definition time:

  1. The function getX is defined during the TDZ, but that's fine
  2. The function getX is called after x is initialized
  3. When getX() runs, x is already available

The TDZ only matters when you actually try to access the variable. Defining a function that will access it later is perfectly safe.

javascript
{
  const getX = () => x  // OK: just defining, not accessing
  
  getX()                // ReferenceError! Calling during TDZ
  
  let x = 42
  
  getX()                // 42 - now it works
}

What Creates a TDZ?

<AccordionGroup> <Accordion title="let declarations"> Every `let` declaration creates a TDZ from the start of its block until the declaration:
```javascript
{
  // TDZ starts
  console.log(x)  // ReferenceError
  let x = 10      // TDZ ends
  console.log(x)  // 10
}
```
</Accordion> <Accordion title="const declarations"> Same behavior as `let`, but `const` must also be initialized at declaration:
```javascript
{
  // TDZ starts
  console.log(PI)  // ReferenceError
  const PI = 3.14159  // TDZ ends
  console.log(PI)  // 3.14159
}
```
</Accordion> <Accordion title="class declarations"> Classes behave like `let` and `const`. You can't use a class before its declaration:
```javascript
const instance = new MyClass()  // ReferenceError

class MyClass {
  constructor() {
    this.value = 42
  }
}

const instance2 = new MyClass()  // Works fine
```

This applies to class expressions too when assigned to `let` or `const`.
</Accordion> <Accordion title="Function default parameters"> Default parameters have their own TDZ rules. Later parameters can reference earlier ones, but not vice versa:
```javascript
// Works: b can reference a
function example(a = 1, b = a + 1) {
  return a + b  // 1 + 2 = 3
}

// Fails: a cannot reference b (TDZ!)
function broken(a = b, b = 2) {
  return a + b  // ReferenceError
}
```
</Accordion> <Accordion title="Static class fields"> Static fields are initialized in order. Later fields can reference earlier ones, but referencing later fields returns `undefined` (not TDZ, since it's property access):
```javascript
class Config {
  static baseUrl = "https://api.example.com"
  static apiUrl = Config.baseUrl + "/v1"  // Works
}

class Example {
  static first = Example.second  // undefined (property doesn't exist yet)
  static second = 10
}
// Example.first is undefined, Example.second is 10
```

However, the class itself is in TDZ before its declaration:

```javascript
const x = MyClass.value  // ReferenceError: MyClass is in TDZ
class MyClass {
  static value = 10
}
```
</Accordion> </AccordionGroup>

TDZ with typeof

Here's a tricky edge case. The typeof operator is normally "safe" to use with undeclared variables:

javascript
console.log(typeof undeclaredVar)  // "undefined" (no error)

But typeof throws a ReferenceError when used on a TDZ variable:

javascript
{
  console.log(typeof x)  // ReferenceError: Cannot access 'x' before initialization
  let x = 10
}

This catches developers off guard because typeof is often used for "safe" variable checking. With let and const, that safety doesn't apply during the TDZ.

<Tip> **Rule of Thumb:** If you need to check whether a variable exists, don't rely on `typeof` alone. Structure your code so variables are declared before you need to check them. </Tip>

TDZ in Destructuring

Destructuring follows the same left-to-right evaluation as default parameters:

javascript
// Works: b can use a's default
let { a = 1, b = a + 1 } = {}
console.log(a, b)  // 1, 2

// Fails: a cannot use b (TDZ!)
let { a = b, b = 1 } = {}  // ReferenceError

Self-referencing is also a TDZ error:

javascript
let { x = x } = {}  // ReferenceError: Cannot access 'x' before initialization

The x on the right side of = refers to the x being declared, which is still in the TDZ.


TDZ in Loops

for...of and for...in Self-Reference

The loop variable is in TDZ during header evaluation:

javascript
// This throws because 'n' is used in its own declaration
for (let n of n.values) {  // ReferenceError
  console.log(n)
}

Fresh Bindings Per Iteration

A key let behavior in loops: each iteration gets a fresh binding:

javascript
const funcs = []

for (let i = 0; i < 3; i++) {
  funcs.push(() => i)
}

console.log(funcs[0]())  // 0
console.log(funcs[1]())  // 1
console.log(funcs[2]())  // 2

With var, all closures share the same variable:

javascript
const funcs = []

for (var i = 0; i < 3; i++) {
  funcs.push(() => i)
}

console.log(funcs[0]())  // 3
console.log(funcs[1]())  // 3
console.log(funcs[2]())  // 3

This fresh binding is why let in loops avoids the classic closure trap.


TDZ in ES Module Circular Imports

ES modules can import each other in a circle. When this happens, TDZ can cause runtime errors that are hard to debug.

The Problem

javascript
// -- a.js (entry point) --
import { b } from "./b.js"

console.log("a.js: b =", b)  // 1

export const a = 2
javascript
// -- b.js --
import { a } from "./a.js"

console.log("b.js: a =", a)  // ReferenceError!

export const b = 1

What Happens

<Steps> <Step title="Start executing a.js"> JavaScript begins running the entry module </Step> <Step title="Pause for import"> It sees `import { b } from "./b.js"` and pauses `a.js` to load the dependency </Step> <Step title="Execute b.js"> JavaScript loads and starts executing `b.js` </Step> <Step title="Create binding to a"> In `b.js`, the `import { a }` creates a binding to `a`, but `a.js` hasn't exported it yet </Step> <Step title="Access a in TDZ"> When `b.js` tries to `console.log(a)`, the variable `a` is still in TDZ (not yet initialized) </Step> <Step title="ReferenceError!"> The TDZ violation throws an error, crashing the application </Step> </Steps>
┌─────────────────────────────────────────────────────────────────────────┐
│                   ES MODULE CIRCULAR IMPORT TDZ                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   EXECUTION ORDER                                                        │
│   ───────────────                                                        │
│                                                                          │
│   1. Start a.js                                                          │
│      │                                                                   │
│      ▼                                                                   │
│   2. import { b } from "./b.js"  ──────┐                                 │
│      [a.js pauses, a not yet exported] │                                 │
│                                        ▼                                 │
│                              3. Start b.js                               │
│                                 │                                        │
│                                 ▼                                        │
│                              4. import { a } from "./a.js"               │
│                                 [a exists but in TDZ!]                   │
│                                 │                                        │
│                                 ▼                                        │
│                              5. console.log(a)                           │
│                                 │                                        │
│                                 ▼                                        │
│                              ReferenceError!                             │
│                              a is in TDZ                                 │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

Solutions

Solution 1: Lazy Access

Don't access the imported value at the top level. Access it inside a function that runs later:

javascript
// -- b.js (fixed) --
import { a } from "./a.js"

// Don't access 'a' immediately
export const b = 1

// Access 'a' later, when it's definitely initialized
export function getA() {
  return a
}

Solution 2: Restructure Modules

Break the circular dependency by extracting shared code:

javascript
// -- shared.js --
export const a = 2
export const b = 1

// -- a.js --
import { a, b } from "./shared.js"

// -- b.js --
import { a, b } from "./shared.js"

Solution 3: Dynamic Import

Use import() to defer loading:

javascript
// -- b.js --
export const b = 1

export async function getA() {
  const { a } = await import("./a.js")
  return a
}
<Warning> **Circular Import Debugging Tip:** If you see a `ReferenceError` for an imported value that you *know* exists, check for circular imports. The error message "Cannot access 'X' before initialization" is the telltale sign of TDZ in modules. </Warning>

The #1 TDZ Mistake: Shadowing

The most common TDZ trap involves variable shadowing:

javascript
// ❌ WRONG - Shadowing creates a TDZ trap
const x = 10

function example() {
  console.log(x)  // ReferenceError! Inner x is in TDZ
  let x = 20      // This shadows the outer x
  return x
}

example()  // ReferenceError!

The inner let x shadows the outer const x. When you try to read x before the inner declaration, JavaScript sees you're trying to access the inner x (which is in TDZ), not the outer one.

┌─────────────────────────────────────────────────────────────────────────┐
│                         TDZ SHADOWING TRAP                               │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   WRONG                                   RIGHT                          │
│   ─────                                   ─────                          │
│                                                                          │
│   const x = 10                            const x = 10                   │
│                                                                          │
│   function broken() {                     function fixed() {             │
│     console.log(x)  // TDZ Error!           const outer = x  // 10       │
│     let x = 20                              let y = 20  // different name│
│     return x                                return outer + y             │
│   }                                       }                              │
│                                                                          │
│   // The inner x shadows the outer        // No shadowing, no TDZ trap   │
│   // but inner x is in TDZ at log                                        │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

How to Avoid It

  1. Use different variable names if you need both the outer and inner values
  2. Capture the outer value first before declaring the inner variable
  3. Structure code so declarations come before usage
javascript
// ✓ CORRECT - Capture outer value before shadowing
const x = 10

function fixed() {
  const outerX = x  // Capture outer x first
  let y = 20        // Use different name, no shadowing
  return outerX + y // Use both: 10 + 20 = 30
}

Why Does TDZ Exist?

TDZ might seem like an annoyance, but it exists for good reasons. According to the TC39 committee notes on ES6 development, the TDZ was introduced specifically to make let and const safer alternatives to var by catching common programming mistakes at the point of error rather than letting them propagate silently:

1. Catches Bugs Early

With var, using a variable before initialization silently gives you undefined:

javascript
function calculateTotal() {
  var total = price * quantity  // undefined * undefined = NaN
  var price = 10
  var quantity = 5
  return total
}

console.log(calculateTotal())  // NaN - silent bug!

With let/const, the bug is caught immediately:

javascript
function calculateTotal() {
  let total = price * quantity  // ReferenceError!
  let price = 10
  let quantity = 5
  return total
}

2. Makes const Semantically Meaningful

If const didn't have a TDZ, you could observe it in an "undefined" state:

javascript
// Hypothetically, without TDZ:
console.log(PI)  // undefined (?)
const PI = 3.14159

That contradicts the purpose of const. A constant should always have its declared value. The TDZ ensures you can never see a const before it has its assigned value.

3. Prevents Reference Before Definition

In languages without TDZ-like behavior, you can accidentally use variables in confusing ways:

javascript
function setup() {
  initialize(config)  // Uses config before it's defined
  const config = loadConfig()
}

The TDZ forces you to organize code logically: definitions before usage.

4. Makes Refactoring Safer

When you move code around, TDZ helps catch mistakes:

javascript
// Original
let data = fetchData()
processData(data)

// After refactoring (accidentally moved)
processData(data)  // ReferenceError - you'll notice immediately!
let data = fetchData()
<Tip> **Think of TDZ as your friend.** It's JavaScript telling you "Hey, you're trying to use something that isn't ready yet. Fix your code structure!" </Tip>

Key Takeaways

<Info> **The key things to remember about the Temporal Dead Zone:**
  1. TDZ = the time between scope entry and variable initialization. During this period, the variable exists but throws ReferenceError when accessed.

  2. let, const, and class have TDZ. var does not. The var keyword initializes to undefined immediately, so there's no dead zone.

  3. "Temporal" means time, not position. A function can reference a TDZ variable if it's called after initialization, even if it's defined before.

  4. typeof is not safe in TDZ. Unlike undeclared variables, typeof on a TDZ variable throws ReferenceError.

  5. Default parameters have TDZ rules. Later parameters can reference earlier ones, but not vice versa.

  6. Circular ES module imports can trigger TDZ. If module A imports from B while B imports from A, one will see uninitialized exports.

  7. Shadowing + TDZ = common trap. When you declare a variable that shadows an outer one, the outer variable becomes inaccessible from the TDZ start.

  8. TDZ catches bugs early. It prevents silent undefined values from causing hard-to-debug issues.

  9. TDZ makes const meaningful. Constants never have a temporary "undefined" state.

  10. Structure code with declarations first. This is the simplest way to avoid TDZ issues entirely.

    </Info>

Test Your Knowledge

<AccordionGroup> <Accordion title="Question 1: What is the Temporal Dead Zone?"> **Answer:**
The Temporal Dead Zone (TDZ) is the period between entering a scope and the point where a variable declared with `let`, `const`, or `class` is initialized. During this period, the variable exists (it's been hoisted) but cannot be accessed. Any attempt to read or write to it throws a `ReferenceError`.

```javascript
{
  // TDZ starts here for 'x'
  console.log(x)  // ReferenceError
  let x = 10      // TDZ ends here
  console.log(x)  // 10
}
```
</Accordion> <Accordion title="Question 2: What's the difference between TDZ and var hoisting?"> **Answer:**
Both `var` and `let`/`const` are hoisted, but they differ in initialization:

- **`var`**: Hoisted AND initialized to `undefined`. No TDZ.
- **`let`/`const`**: Hoisted but NOT initialized. TDZ until declaration.

```javascript
console.log(x)  // undefined (var is initialized)
var x = 10

console.log(y)  // ReferenceError (let is in TDZ)
let y = 10
```
</Accordion> <Accordion title="Question 3: Why does typeof throw in TDZ but not for undeclared variables?"> **Answer:**
For **undeclared** variables, `typeof` returns `"undefined"` as a safety feature. For **TDZ** variables, JavaScript knows the variable exists (it's been hoisted), so it enforces the TDZ restriction.

```javascript
console.log(typeof undeclared)  // "undefined" (safe)

{
  console.log(typeof x)  // ReferenceError (TDZ enforced)
  let x = 10
}
```

The difference is: undeclared means "doesn't exist," while TDZ means "exists but not ready."
</Accordion> <Accordion title="Question 4: Can a function reference a TDZ variable?"> **Answer:**
**Yes, but only if the function is called after the variable is initialized.** Defining the function during TDZ is fine. Calling it during TDZ throws an error.

```javascript
{
  const getX = () => x  // OK: defining, not accessing
  
  // getX()  // Would throw: x is in TDZ
  
  let x = 42            // TDZ ends
  
  console.log(getX())   // 42: called after TDZ
}
```

This is why it's called "temporal" (time-based), not "positional" (code-position-based).
</Accordion> <Accordion title="Question 5: What happens with let x = x?"> **Answer:**
It throws a `ReferenceError`. The `x` on the right side refers to the `x` being declared, which is still in TDZ at the time of evaluation.

```javascript
let x = x  // ReferenceError: Cannot access 'x' before initialization
```

This also applies in destructuring:

```javascript
let { x = x } = {}  // ReferenceError
```
</Accordion> <Accordion title="Question 6: Why does TDZ exist?"> **Answer:**
TDZ exists for several reasons:

1. **Catch bugs early**: Using uninitialized variables throws immediately instead of silently returning `undefined`

2. **Make `const` meaningful**: Constants should always have their declared value, never a temporary `undefined`

3. **Enforce logical code structure**: Encourages declaring variables before using them

4. **Safer refactoring**: Moving code around reveals dependency issues immediately

```javascript
// Without TDZ (var), this bug is silent:
var total = price * quantity  // NaN
var price = 10
var quantity = 5

// With TDZ (let), the bug is caught:
let total = price * quantity  // ReferenceError!
let price = 10
let quantity = 5
```
</Accordion> </AccordionGroup>

Frequently Asked Questions

<AccordionGroup> <Accordion title="What is the Temporal Dead Zone in JavaScript?"> The Temporal Dead Zone (TDZ) is the period between entering a scope and reaching the declaration of a `let`, `const`, or `class` variable. During this window, the variable exists but any access throws a `ReferenceError`. MDN describes it as the zone where bindings are "created but not yet initialized." </Accordion> <Accordion title="Does typeof work safely in the Temporal Dead Zone?"> No. Unlike undeclared variables where `typeof` safely returns `"undefined"`, using `typeof` on a TDZ variable throws a `ReferenceError`. This is because the JavaScript engine knows the variable exists (it was hoisted) but enforces the TDZ restriction. The ECMAScript specification explicitly requires this behavior for `let` and `const` bindings. </Accordion> <Accordion title="Is the Temporal Dead Zone based on code position or execution time?"> It is based on execution time, which is why it is called "temporal." A function defined during the TDZ can reference a TDZ variable safely, as long as the function is called after the variable is initialized. MDN documents this distinction: the zone depends on the order of execution, not the order of code. </Accordion> <Accordion title="How does TDZ affect circular ES module imports?"> When two modules import each other, one will execute before the other has finished its exports. If module B tries to access an exported `const` from module A before A has reached that declaration, it triggers a TDZ `ReferenceError`. The fix is to access the import lazily inside a function rather than at the top level. </Accordion> <Accordion title="Can you avoid the Temporal Dead Zone?"> The simplest way to avoid TDZ issues is to always declare variables at the top of their scope before using them. The TDZ is a feature, not a bug — the 2023 Stack Overflow Developer Survey shows that over 87% of JavaScript developers prefer `let` and `const` over `var`, accepting the TDZ trade-off for safer code. </Accordion> </AccordionGroup>
<CardGroup cols={2}> <Card title="Scope and Closures" icon="layer-group" href="/concepts/scope-and-closures"> The foundation for understanding TDZ: how JavaScript determines variable visibility </Card> <Card title="Hoisting" icon="arrow-up" href="/beyond/concepts/hoisting"> How JavaScript moves declarations to the top of their scope </Card> <Card title="ES Modules" icon="boxes-stacked" href="/concepts/es-modules"> How TDZ interacts with circular module imports </Card> <Card title="Strict Mode" icon="shield" href="/beyond/concepts/strict-mode"> Another feature that helps catch errors early </Card> </CardGroup>

Reference

<CardGroup cols={2}> <Card title="let — MDN" icon="book" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let"> Official documentation covering TDZ behavior for let declarations </Card> <Card title="const — MDN" icon="book" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const"> How const declarations interact with the Temporal Dead Zone </Card> <Card title="class — MDN" icon="book" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class"> Class declarations and their TDZ behavior </Card> <Card title="ReferenceError — MDN" icon="book" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError"> The error thrown when accessing TDZ variables </Card> </CardGroup>

Articles

<CardGroup cols={2}> <Card title="ES6 In Depth: let and const — Mozilla Hacks" icon="newspaper" href="https://hacks.mozilla.org/2015/07/es6-in-depth-let-and-const/"> Historical context from Mozilla engineers on why TDZ was introduced. Explains the design decisions behind let and const. </Card> <Card title="What is the Temporal Dead Zone? — Stack Overflow" icon="newspaper" href="https://stackoverflow.com/questions/33198849/what-is-the-temporal-dead-zone"> The canonical community explanation with examples and edge cases discussed by experienced developers. </Card> <Card title="You Don't Know JS: Scope & Closures — Kyle Simpson" icon="newspaper" href="https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/scope-closures/ch5.md"> Deep dive into variable lifecycle and TDZ from the popular book series. Free to read on GitHub. </Card> <Card title="JavaScript Variables: var, let, and const — freeCodeCamp" icon="newspaper" href="https://www.freecodecamp.org/news/var-let-and-const-whats-the-difference/"> Beginner-friendly comparison of all three declaration types with clear TDZ examples. </Card> </CardGroup>

Videos

<CardGroup cols={2}> <Card title="let & const in JS: Temporal Dead Zone — Akshay Saini" icon="video" href="https://www.youtube.com/watch?v=BNC6slYCj50"> Visual explanation of TDZ with diagrams showing exactly when variables become accessible. Part of the popular Namaste JavaScript series. </Card> <Card title="var, let and const — Web Dev Simplified" icon="video" href="https://www.youtube.com/watch?v=9WIJQDvt4Us"> Clear comparison of all three declaration types in under 10 minutes. Great for beginners who want a quick overview. </Card> </CardGroup>
<Card title="Back to Overview" icon="arrow-left" href="/beyond/getting-started/overview"> Return to the Beyond 33 overview </Card>