Hoisting is one of those JavaScript behaviors that confuses a lot of beginners. In simple language, hoisting means JavaScript moves declarations to the top of their scope before the code actually runs. But the key detail is — only declarations are hoisted, not initializations.
Think of it like this: before your code runs, JavaScript does a quick scan and says “okay, I see these variables and functions exist” — but it doesn’t assign values yet (for var).
var hoisting
Variables declared with var are hoisted to the top of their function scope and initialized with undefined. This means we can access them before the line where they are declared, but the value will be undefined.
console.log(name); // undefined (not an error!)
var name = "Manish";
console.log(name); // "Manish"
What JavaScript actually sees during execution:
var name; // declaration hoisted, initialized with undefined
console.log(name); // undefined
name = "Manish"; // assignment stays in place
console.log(name); // "Manish"
let and const hoisting (Temporal Dead Zone)
let and const are also hoisted — but they are not initialized. They sit in something called the Temporal Dead Zone (TDZ) from the start of the block until the declaration line. Trying to access them in the TDZ throws a ReferenceError.
let x = 10;
-- TDZ ends here
// console.log(age); // ReferenceError: Cannot access 'age' before initialization
let age = 25;
console.log(age); // 25
The same applies to const. The important thing to remember is — let and const are hoisted (JavaScript knows they exist), but they are not accessible until the declaration line.
Function declaration hoisting
Function declarations are fully hoisted — both the name and the body. This means we can call a function before it appears in the code. This is the one case where hoisting actually feels useful.
greet(); // "Hello!" — works perfectly!
function greet() {
console.log("Hello!");
}
Function expressions and arrow functions are NOT hoisted
Function expressions (including arrow functions) behave like variable assignments. The variable is hoisted according to var/let/const rules, but the function body is not. So we cannot call them before the assignment.
greet(); // "Hello!"
function greet() { console.log("Hello!"); }
// sayHi(); // ReferenceError
const sayHi = () => { console.log("Hi!"); };
// hello(); // TypeError / ReferenceError
const hello = function() { console.log("Hello!"); };
If we used var instead of const, we would get a TypeError (because the variable is undefined, and undefined is not a function). With const/let, we get a ReferenceError because of the Temporal Dead Zone.
// sayHi(); // TypeError: sayHi is not a function
var sayHi = () => { console.log("Hi!"); };
// greetFn(); // ReferenceError: Cannot access 'greetFn' before initialization
const greetFn = () => { console.log("Hey!"); };
Quick summary
In simple language, only function declarations are fully hoisted and usable before their line. Everything else either gives undefined (var) or throws an error (let/const). When in doubt, just declare things before you use them and you will never have hoisting issues.