Academic Block

Hoisting Get Certified

Hoisting is JavaScript’s behavior of moving declarations to the top of their scope before the code actually runs. Understanding it explains why you can sometimes use a function before you write it, why var gives you undefined instead of an error, and why let, const, and classes throw a special “Temporal Dead Zone” error.

Function Declarations Are Fully Hoisted

A function declaration is completely available from the top of its scope. You can call it before the line where it is written.

What this code does: Calls a function before its declaration appears in the code. Because function declarations are fully hoisted, the call works.

console.log(greet("Michael"));

function greet(name) {
    return "Hello, " + name;
}

var Is Hoisted and Initialized to undefined

Variables declared with var are hoisted to the top of their function (or global) scope and automatically given the value undefined until the assignment line runs.

What this code does: Reads a var before its assignment. The variable exists but its value is still undefined.

console.log(city);          // undefined (not an error)

var city = "New York";

console.log(city);          // "New York"

let and const Have a Temporal Dead Zone

let and const are also hoisted, but they stay uninitialized until their declaration line runs. Accessing them earlier throws a ReferenceError. This period is called the Temporal Dead Zone (TDZ).

What this code does: Tries to read a let variable before its declaration. The Temporal Dead Zone causes a ReferenceError.

try {
    console.log(city);
    let city = "New York";
} catch (error) {
    console.log("Error:", error.message);
}

What this code does: Same experiment with const. It also stays in the Temporal Dead Zone until the declaration runs.

try {
    console.log(country);
    const country = "United States";
} catch (error) {
    console.log("Error:", error.message);
}

The important detail here is that let and const are hoisted — they are just left uninitialized until their declaration runs, unlike var.

Function Expressions Are Not Hoisted Like Declarations

A function expression assigned to a var follows var’s hoisting rules — the variable itself is hoisted as undefined, but the function value is not available until the assignment line runs.

What this code does: Shows that a function expression assigned to var is only the variable that is hoisted. Before the assignment, typeof reports "undefined".

console.log(typeof greet);   // "undefined"

var greet = function () {
    return "Hi!";
};

console.log(greet());        // "Hi!"

Calling It Too Early

Since sayHi is undefined before its assignment runs, trying to call it as a function throws a TypeError, not a ReferenceError.

What this code does: Attempts to call a function expression before its assignment. Because the variable is still undefined, a TypeError is thrown.

try {
    sayHi();
} catch (error) {
    console.log("Error:", error.message);
}

var sayHi = function () {
    return "Hi!";
};

Function Expressions with let

The same function-expression rule applies to let and const, but because they have a Temporal Dead Zone, calling them too early throws a ReferenceError instead of a TypeError.

What this code does: Tries to call an arrow function assigned to let before its declaration. The Temporal Dead Zone causes a ReferenceError.

try {
    console.log(sayHello());
} catch (error) {
    console.log("Error:", error.message);
}

let sayHello = () => "Hello!";

Classes Are Hoisted, But Also Have a TDZ

Like let and const, classes are hoisted to the top of their scope but remain in the Temporal Dead Zone until the class declaration itself is evaluated.

What this code does: Attempts to instantiate a class before its declaration. Classes are hoisted but stay in the Temporal Dead Zone, so a ReferenceError is thrown.

try {
    const person = new Person("Michael");
    console.log(person.name);
} catch (error) {
    console.log("Error:", error.message);
}

class Person {
    constructor(name) {
        this.name = name;
    }
}

var Hoists to the Function, Not the Block

A var declared inside a block, like an if statement, is hoisted all the way to the top of the enclosing function — not just to the top of that block.

What this code does: Declares a var inside an if block. Because var is function-scoped, the variable is still accessible after the block.

function demo() {
    if (true) {
        var message = "Hello from inside the block";
    }
    console.log(message);
}
demo();

let and const Stay Inside Their Block

Unlike var, let and const do not exist at all outside the block where they were declared.

What this code does: Declares a let inside a block. Outside that block the variable is not accessible, so a ReferenceError is thrown.

function demo() {
    if (true) {
        let message = "Hello from inside the block";
        console.log(message);
    }
    try {
        console.log(message);
    } catch (error) {
        console.log("Error:", error.message);
    }
}
demo();

Function Declarations Take Priority Over var

When a var declaration and a function declaration share the same name, the function declaration’s hoisting wins first — until the var assignment line actually runs and overwrites it.

What this code does: Shows that a function declaration is fully hoisted and wins over a same-named var until the assignment line runs and overwrites it.

console.log(typeof example);   // "function"

var example = "a string value";

function example() {
    return "a function value";
}

console.log(typeof example);   // "string"

At the top, example is already a function, because function declarations are hoisted with their full definition. Only after var example = "a string value"; runs does it become a string.

Hoisting Behavior Summary

Declaration Type Hoisted? Usable Before Its Line?
Function declaration Yes Yes — fully usable
var Yes Value is undefined until assigned
let Yes No — Temporal Dead Zone (ReferenceError)
const Yes No — Temporal Dead Zone (ReferenceError)
class Yes No — Temporal Dead Zone (ReferenceError)
Function expression (var greet = function(){}) Only the variable, not the function value No — value is undefined or in the TDZ

Common Mistakes with Hoisting

  • Assuming let and const are not hoisted at all — they are, but they stay uninitialized until their declaration runs.
  • Confusing a function declaration with a function expression and expecting both to be callable before their line.
  • Relying on var hoisting for code that reads more clearly if variables are simply declared at the top.
  • Forgetting that a var inside a block is actually scoped to the whole function, which can cause confusing bugs in loops and conditionals.
  • Assuming classes behave like function declarations — they do not, since they also have a Temporal Dead Zone.

Best Practices for Hoisting

  • Declare variables and functions before using them, even though hoisting would sometimes allow otherwise — it keeps code easier to follow.
  • Prefer let and const over var; their Temporal Dead Zone helps catch accidental early use as an error instead of silently returning undefined.
  • Declare each variable in the smallest scope where it is needed, close to where it is first used.
  • Avoid giving a function declaration and a variable the same name in the same scope.
  • Treat function expressions and arrow functions as unavailable until the line that defines them actually runs.

Quick Summary

  • Hoisting moves declarations to the top of their scope before code runs — but not always their assigned values.
  • Function declarations are hoisted with their full definition and can be called before their line.
  • var is hoisted and initialized to undefined until assigned.
  • let, const, and classes are hoisted but stay in the Temporal Dead Zone until their declaration runs.
  • Function expressions and arrow functions follow the hoisting rules of whatever keyword (var, let, or const) they are assigned to.
  • var hoists to the enclosing function, while let and const stay inside their block.

Try It Yourself

Modify the code below. Try changing let to var to see the error disappear, or moving the function declaration to see if the order still matters.

What this code does: Practice example that combines a fully-hoisted function declaration with a let that is still in the Temporal Dead Zone. Experiment by swapping keywords or moving lines.

console.log(multiply(2, 3));   // 6 – function is fully hoisted

try {
    console.log(count);        // ReferenceError (TDZ)
    let count = 10;
} catch (error) {
    console.log("Error:", error.message);
}

function multiply(a, b) {
    return a * b;
}
Tip: Remember that “hoisted” does not mean “usable” — function declarations are usable immediately, var is usable but undefined, and let, const, and classes are hoisted straight into the Temporal Dead Zone.

Understanding hoisting explains behavior you have already seen with var, let, and const, and clears up why some errors say "Cannot access before initialization" instead of simply "is not defined".

🧪 Test Your JavaScript Code

Edit the code on the left and click “Run Code” to see the console output on the right.

📝 JavaScript Code
👁 Console Output

Click “Run Code” to see the output here.

Ctrl + Enter to run code Esc to close