# Hoisting, var and the Temporal Dead Zone

Before JavaScript runs a scope it notes every declaration in it, which is why a function can be called above the line that makes it, why var gives undefined there, and why let and const stop with an error instead.

Journeyman · JavaScript · Updated November 1, 2026

## After this lesson you can

- Predict what happens when code uses a variable or function before the line that declares it
- Explain how var differs from let and const, and rewrite older var code with let or const

## Key takeaways

- Before a scope runs, JavaScript notes every declaration in it. This is called hoisting.
- A function declaration is ready from the first line of its scope, so you can call it above the line that makes it.
- A var used before its line gives undefined. A let or const used before its line stops with a ReferenceError: the temporal dead zone.
- var belongs to the whole function, not to the block it's written in, and it can be declared twice without an error.
- In new code, use const, and let when the value must change. Read var in older code as "belongs to the whole function".

Search the web for almost any JavaScript question and you'll find answers written with `var` instead of `let` and `const`. You'll meet it in tutorials, in a class's sample code and in the code of almost any company that has had a website for a while. `var` follows older rules, and those rules explain a puzzle you may have already noticed: you can call a function on line 1 that isn't written until line 20, but a `let` used one line too early stops the program. This lesson explains both, so you can read older code and know what to write instead.

## Before the first line runs

Before the Starlight Arcade opens each morning, the manager walks the floor and puts a name card on every spot where a machine belongs: *Space Blaster goes here, Skee-Ball goes there*. Some machines are already plugged in; some are still in their boxes. But from the moment the doors open, the staff know every machine the arcade has.

JavaScript does the same. Before it runs the first line of a scope, it reads the whole scope and notes every declaration in it: every `function`, `var`, `let` and `const`. Only then does it run the lines, top to bottom. This is called **hoisting**, from "hoist", to lift up: it behaves as if the declarations were lifted to the top of their scope. Nothing really moves in your file; JavaScript knows the names early.

What each name holds at that early moment depends on how it was declared, and that's the rest of this lesson.

## Function declarations are ready early

A **function declaration** is a function made with the word `function` and a name, as its own statement. It's plugged in before the doors open: you can call it from any line in its scope, even above the line that makes it.

```javascript
console.log(tokensFor(5));

function tokensFor(dollars) {
  return dollars * 4;
}
```

```text
20
```

That's handy: a file can start with the main steps of a program and keep the helper functions at the bottom, out of the way.

## var: a name card with nothing on it yet

A variable made with `var` is hoisted too, but only its name. Its value arrives when its line runs. Used earlier than that, it holds `undefined`, the value JavaScript gives a variable that has no value yet:

```javascript
console.log(`Tokens: ${tokens}`);

var tokens = 20;

console.log(`Tokens: ${tokens}`);
```

```text
Tokens: undefined
Tokens: 20
```

No error, no warning: the program carries on with `undefined`, and the trouble shows up later, somewhere else. A bug that hides like that is hard to find.

## let and const: the temporal dead zone

`let` and `const` are hoisted as well, so JavaScript knows their names from the start of the block. But it won't let any line touch them until their own line has run. "Temporal" means "to do with time", and the stretch of time from the start of the block until the declaration runs is called the **temporal dead zone** (often shortened to **TDZ**). Use the variable inside it, and the program stops with a clear message:

```javascript
console.log(`Tickets: ${tickets}`);

let tickets = 50;
```

```text
C:\arcade\early.js:1
console.log(`Tickets: ${tickets}`);
                        ^

ReferenceError: Cannot access 'tickets' before initialization
```

"Initialization" means giving a variable its first value. Compare this message with the one for a name that doesn't exist at all, `is not defined`: here JavaScript is saying "I know this name, but you're too early." A loud error on the right line is much kinder than a quiet `undefined`, which is one reason `let` and `const` replaced `var`.

A function made with `const` and an arrow follows the same rule: `const tokensFor = (dollars) => dollars * 4;` can't be called above its line. Only function declarations are ready early.

**Try it.** Save the `tickets` code as `early.js` and run it with `node early.js`. Then change `let` to `var` and run it again. Which do you find easier to fix: the error or the `undefined`?

## var belongs to the whole function

The other big difference: `var` ignores blocks. A `var` made inside an `if` or a loop belongs to the whole function around it (or to the whole file, at the top level). In the house picture from *Scope: Where a Variable Can Be Seen*, a `var` never stays in the closet; it always moves out into the room.

```javascript
function checkPrize(tickets) {
  if (tickets >= 100) {
    var prize = 'giant stuffed panda';
  }
  console.log(`Prize: ${prize}`);
}

checkPrize(150);
checkPrize(40);

for (var round = 1; round <= 3; round++) {
  console.log(`Round ${round}`);
}
console.log(`After the loop, round is ${round}`);
```

```text
Prize: giant stuffed panda
Prize: undefined
Round 1
Round 2
Round 3
After the loop, round is 4
```

With `let` or `const`, `prize` and `round` would belong to their blocks, and both lines outside the blocks would stop with a ReferenceError. With `var`, `prize` exists everywhere in the function (holding `undefined` when the `if` didn't run), and `round` is still there after the loop, holding the 4 that ended it. `var` also lets you declare the same name twice in one scope, with no error, where `let` stops with *Identifier 'playerName' has already been declared*.

## Why var is still around, and what to write instead

Until 2015, `var` was the only way to make a variable in JavaScript. That year's version of the language (ES2015, often called ES6) added `let` and `const`, and nobody could remove `var` without breaking millions of websites that still use it. So both stay.

| | `var` | `let` | `const` |
|---|---|---|---|
| Belongs to | the whole function | its block | its block |
| Used before its line | `undefined` | ReferenceError (the TDZ) | ReferenceError (the TDZ) |
| Declared twice in one scope | allowed | an error | an error |
| Given a new value later | allowed | allowed | an error |

When you read older code, read each `var` as "this belongs to the whole function". When you write or update code, use `const` by default, and `let` when the value must change:

```javascript
function totalTickets(games) {
  let total = 0;
  for (let i = 0; i < games.length; i++) {
    const tickets = games[i] * 2;
    total = total + tickets;
  }
  return total;
}

console.log(totalTickets([10, 25, 5]));
```

```text
80
```

This was `var total`, `var i` and `var tickets` before. `total` changes, so it becomes `let`; `i` changes every time around the loop, so `let`; and `tickets` gets one value each time around, so `const`.

**Try it.** Write a function with `var` in an `if` block, as `checkPrize` does, and change the `var` to `let`. Run it and read the error. Then move the line that uses the variable inside the block, where it belongs.

## Check yourself

2 questions to try on the lesson page.

## So what

Hoisting explains why a function works above its line and a `let` doesn't, and `var`'s rules explain a lot of surprising older code. You'll read `var` for years; write `const` and `let`, and the temporal dead zone will catch your too-early mistakes for you.

## Key terms

- [Hoisting](https://www.ask-codey.com/glossary/hoisting)
- [var](https://www.ask-codey.com/glossary/var-javascript)
- [let](https://www.ask-codey.com/glossary/let)
- [const](https://www.ask-codey.com/glossary/const-javascript)
- [Temporal Dead Zone](https://www.ask-codey.com/glossary/temporal-dead-zone)
- [Function Declaration](https://www.ask-codey.com/glossary/function-declaration)

## Cheat sheets

- [Hoisting, var and the Temporal Dead Zone](https://www.ask-codey.com/cheat-sheets/javascript-scope-hoisting-and-var-cheat-sheet)

## Related

- [Scope: Where a Variable Can Be Seen](https://www.ask-codey.com/lessons/javascript-scope-where-variables-live)

Source: https://www.ask-codey.com/lessons/javascript-scope-hoisting-and-var
