Chapter IX · Scope

Hoisting

Hoisting describes how JavaScript sets up var and function declarations before running any code in a scope — as if they were physically moved to the top of that scope, even though the source code lists them later.

01 How It Works

Think of hoisting like a stage crew that sets every prop in its marked spot before the curtain rises — the props (variable names) are physically in place from the very first scene, even though an actor doesn't hand them their actual contents (the value) until their scripted moment arrives.

Before running any code, the engine processes a scope in a creation phase: it scans for var and function declarations and sets up bindings for them immediately. Function declarations are hoisted completely — their entire body is ready to call from the very first line. var declarations are hoisted only as a name, automatically initialized to undefined, with the actual assignment (= 5) left in place to run later, during the execution phase, when its line is actually reached. let, const, and class are also detected during the creation phase (the engine knows they exist), but they are deliberately left uninitialized rather than set to undefined — accessing them before their declaration line throws, which is the Temporal Dead Zone.

example.js
console.log(greet()); // "Hi!" — works, even though greet is defined below
console.log(x);       // undefined — not an error, just not assigned yet
var x = 5;

function greet() {
  return "Hi!";
}
  1. 1. Creation phase: the engine scans the scope, sets up var names as undefined, and fully defines any function declarations.

  2. 2. Execution phase begins: code now runs top to bottom, in the order it's written.

  3. 3. When execution reaches a var's own assignment line, the real value finally replaces the undefined placeholder.

02 Practical Example

Here is how you might see this concept applied in real-world code:

practical.js
// Function declarations: fully hoisted, safe to call early
sayHi(); // "hi"
function sayHi() { console.log("hi"); }

// var: hoisted as a name, but NOT its value
console.log(count); // undefined, not a ReferenceError
var count = 10;

// Common mistake: function EXPRESSIONS are not hoisted the same way —
// only the "var" name is hoisted, not the function it's assigned to.
console.log(typeof sayBye); // "undefined"
sayBye(); // TypeError: sayBye is not a function
var sayBye = function () { console.log("bye"); };

Key Takeaway

Hoisting only lifts the declaration, never the assignment — var gets an early undefined placeholder, function declarations get their whole body upfront, and let/const/class are tracked but locked behind the Temporal Dead Zone.

Related Concepts