JavaScriptHoisting

Hoisting

Hoisting is the name for JavaScript's habit of acting as if certain declarations were moved to the top of their scope. It is not a literal rewrite of your code — it is a side-effect of how the engine sets up a scope before running it. Understanding hoisting clears up a lot of "why does this run but throw undefined?" confusion.

The mental model: two passes

Before executing a block, the engine does a quick first pass to find every declaration:

  • Function declarations are fully registered — both the name and the function body — before any code runs.

  • var declarations are registered with the value undefined. The assignment itself stays where you wrote it.

  • let and const declarations are registered but uninitialised — reading them before the line where they are declared throws (this is the Temporal Dead Zone).

Function declarations — fully hoisted

JS
hello();   // works fine — prints "hi"

function hello() {
  console.log("hi");
}

Both the name hello and its body are available from the start of the scope. This is what makes the classic "list helper functions below the main code" style legal.

`var` — name hoisted, value isn't

JS
console.log(x);   // undefined — not a ReferenceError
var x = 5;
console.log(x);   // 5

The first line works because var x is registered at the top of the scope with the value undefined. The = 5 part runs on the third line. This is almost never what you want, and is one reason modern code prefers let/const.

What the engine effectively sees

JS
var x;             // hoisted to the top, value: undefined
console.log(x);    // undefined
x = 5;             // assignment stays here
console.log(x);    // 5
Function expressions are not function declarations

Only the function name() {} statement form is hoisted with its body. A function expression assigned to a variable follows the rules of that variable.

JS
// hi();             // TypeError — hi is undefined right now
var hi = function () { console.log("hi"); };
hi();                // works after the assignment

JS
// bye();             // ReferenceError — TDZ
const bye = function () { console.log("bye"); };
bye();
`let` and `const` — the Temporal Dead Zone

let and const are hoisted in the sense that the engine knows about them, but they remain uninitialised until the line that declares them runs. Any access before that line throws. This window is called the Temporal Dead Zone (TDZ).

JS
console.log(name);   // ReferenceError: Cannot access 'name' before initialization
let name = "Ada";

The TDZ exists on purpose — it catches bugs where you accidentally read a variable above where you meant to declare it.

`typeof` is not safe in the TDZ
In the TDZ, even `typeof x` throws. With `var`, `typeof x` of an undeclared variable returns `"undefined"` — a quirk that does **not** apply to `let` and `const`.
Why hoisting feels surprising

The most common confusion comes from mixing function declarations and expressions inside the same file. The two look almost identical but behave very differently when used before their definition.

JS
run();
function run() { console.log("a"); }   // ok — declaration

// crash();
// function crash() { ... } overwritten later? Let's see:
function crash() { console.log("first"); }
function crash() { console.log("second"); }   // wins — later declaration overrides
crash();    // "second"
Practical rules
  • Use let and const everywhere. The TDZ turns a silent bug into a clear error.

  • Declare variables before you use them, even though function declarations technically allow the opposite. It reads better.

  • If you depend on hoisting to make a file readable (helper functions below main code), that is fine — function declarations are designed to support that.

  • Be wary of var in legacy code: it does not respect block scope (only function scope) and is hoisted with undefined.

In one sentence
Hoisting is just the engine setting up bindings before running code — function declarations come fully loaded, `var` comes as `undefined`, and `let`/`const` come unreadable until their line runs.