Hoisting pitfalls and how to avoid them

JavaScript

The short answer

Hoisting moves variable and function declarations to the top of their scope during compilation. The pitfalls come from the fact that var declarations are hoisted but initialized as undefined, while let and const are hoisted but not initialized at all (they sit in a "temporal dead zone"). Function declarations are fully hoisted, but function expressions are not.

Pitfall 1: Using var before declaration

console.log(name); // undefined — not an error!
var name = 'John';
console.log(name); // "John"

JavaScript hoists var name to the top, but not the assignment. So the code actually runs like this:

var name; // hoisted — initialized as undefined
console.log(name); // undefined
name = 'John';
console.log(name); // "John"

This can cause silent bugs. You expect an error but get undefined instead, and the bug might go unnoticed.

Fix: Use let or const instead of var:

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

With let and const, you get a clear error. This is much better because you catch the bug immediately.

Pitfall 2: The Temporal Dead Zone (TDZ)

let and const are hoisted, but they are not initialized. From the start of the block to the declaration, they are in a "temporal dead zone" where accessing them throws an error.

{
// TDZ starts
console.log(x); // ReferenceError
let x = 10; // TDZ ends
console.log(x); // 10
}

This is actually a good thing. The TDZ forces you to declare variables before using them, which leads to cleaner code.

Pitfall 3: Function expressions are not hoisted like declarations

Function declarations are fully hoisted — you can call them before they appear in the code:

greet(); // "Hello" — works fine
function greet() {
console.log('Hello');
}

But function expressions (where you assign a function to a variable) follow the rules of the variable:

greet(); // TypeError: greet is not a function
var greet = function () {
console.log('Hello');
};

var greet is hoisted as undefined. Calling undefined() gives a TypeError. If you use let or const, you get a ReferenceError instead (which is more helpful).

Pitfall 4: var in loops

for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 100);
}
// Output: 3, 3, 3 (not 0, 1, 2)

var is function-scoped, not block-scoped. There is only one i variable for the entire loop. By the time the setTimeout callbacks run, i is already 3.

Fix: Use let — it is block-scoped, so each iteration gets its own i:

for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 100);
}
// Output: 0, 1, 2

Pitfall 5: Hoisting in conditional blocks

With var, declarations are hoisted out of if blocks:

function test() {
console.log(x); // undefined — not an error
if (false) {
var x = 10; // hoisted even though this block never runs
}
console.log(x); // undefined
}

The var x is hoisted to the top of the function even though the if block never executes. This is confusing and can lead to unexpected behavior.

How to avoid hoisting pitfalls

  1. Always use let and const — never use var. This is the single most important rule.
  2. Declare variables at the top of their scope — even with let/const, it is good practice to declare them before you use them.
  3. Use const by default — only use let when you know the value will change. This prevents accidental reassignment.
  4. Use function declarations for named functions — they are fully hoisted, which is predictable. Use arrow functions for callbacks.

Common Pitfalls

A common mistake in interviews is saying that let and const are not hoisted. They are hoisted — JavaScript knows about them. But they are not initialized, so accessing them before the declaration throws a ReferenceError. The correct term is "temporal dead zone." Saying they are not hoisted at all is technically incorrect.

Interview Tip

When answering hoisting questions, always compare var, let, and const. Show what happens with each when you access the variable before its declaration. The key takeaway is: var gives you undefined (silent bug), let/const give you an error (loud bug). Loud bugs are better because you catch them right away.

Why interviewers ask this

Hoisting is one of the quirky parts of JavaScript that causes real bugs. Interviewers want to see if you understand why var can be dangerous, if you know what the temporal dead zone is, and if you follow best practices like using let and const. It also tests your understanding of how JavaScript compiles and executes code, which is a fundamental concept.