Hoisting
Why you can use functions before declaring them, and why var behaves differently from let/const
Last updated on
Hoisting is JavaScript's behavior of moving declarations to the top of their scope during the creation phase. It's not literally moving code — it's about how memory is allocated before execution.
The Core Concept
// You might think this should error:
console.log(greeting);
var greeting = "Hello";
// But it prints: undefinedWhat the engine effectively does:
var greeting; // declaration hoisted to top
console.log(greeting); // undefined
greeting = "Hello"; // assignment stays in placevar Hoisting
var declarations are hoisted and initialized as undefined:
console.log(a); // undefined
var a = 5;
console.log(a); // 5let / const Hoisting (Temporal Dead Zone)
let and const ARE hoisted, but they're placed in a Temporal Dead Zone (TDZ) — they exist in memory but are not accessible until the declaration line:
console.log(b); // ❌ ReferenceError: Cannot access 'b' before initialization
let b = 10;{ // scope starts — TDZ begins for 'b'
// ... accessing 'b' here throws ReferenceError
let b = 10; // TDZ ends — 'b' is now initialized
console.log(b); // ✅ 10
}Function Declaration Hoisting
Entire function declarations (body and all) are hoisted:
sayHello(); // ✅ "Hello!" — works even before the declaration
function sayHello() {
console.log("Hello!");
}Function Expression — NOT Hoisted
sayHello(); // ❌ TypeError: sayHello is not a function (with var)
// ❌ ReferenceError (with let/const)
var sayHello = function () {
console.log("Hello!");
};With var, sayHello is hoisted as undefined, so calling it throws TypeError: undefined is not a function.
Class Hoisting
Classes are hoisted but NOT initialized (like let):
const p = new Person(); // ❌ ReferenceError
class Person {
constructor() {
this.name = "Shiva";
}
}Hoisting Priority
When both a variable and function share the same name:
console.log(typeof foo); // "function"
var foo = "hello";
function foo() {
return "world";
}
console.log(typeof foo); // "string"Rule: Function declarations are hoisted above var declarations.
Interview Output Questions
Question 1
var x = 1;
function test() {
console.log(x);
var x = 2;
console.log(x);
}
test();Answer: undefined, then 2.
The inner var x is hoisted within test(), shadowing the outer x. During the creation phase of test(), x is undefined.
Question 2
foo();
bar();
function foo() {
console.log("foo");
}
var bar = function () {
console.log("bar");
};Answer: "foo" prints, then TypeError: bar is not a function.
foo is a declaration (fully hoisted). bar is a var expression (hoisted as undefined).
Question 3
console.log(a);
console.log(b);
console.log(c);
var a = 1;
let b = 2;
const c = 3;Answer:
a→undefinedb→ReferenceError(stops execution —cnever runs)
Question 4
function test() {
console.log(a); // ?
console.log(fn()); // ?
var a = 10;
function fn() {
return 20;
}
}
test();Answer: undefined, then 20.
The Mental Model
What you write: What JS "sees":
var a; ← var hoisted
function fn(){} ← function hoisted
// let/const in TDZ
console.log(a); console.log(a);
let b = 2; let b = 2; ← TDZ ends
var a = 1; a = 1; ← assignment
function fn() {} // (already hoisted above)Best Practices to Avoid Hoisting Confusion
- Use
constandlet— TDZ catches bugs immediately - Declare variables at the top of their scope
- Define functions before using them — even though declarations are hoisted
- Never rely on hoisting — write code as if it doesn't exist