Hoisting is JavaScript's behavior of moving declarations to the top of their scope during compilation — var is initialized as undefined, let/const enter a Temporal Dead Zone, and function declarations are fully available before their code position.
Declaration is hoisted and initialized to undefined; function-scoped, ignores block scope entirely
Hoisted but not initialized — accessing before declaration throws ReferenceError due to the Temporal Dead Zone
Fully hoisted with name and body — can be called anywhere in scope before the declaration line
Follow the hoisting rules of their variable keyword — var gives undefined, let/const gives ReferenceError
Classes behave like let/const — hoisted into the TDZ, cannot be instantiated before declaration
Hoisting is the JavaScript engine's behavior during the compilation phase where variable and function declarations are processed before any code executes. The declarations appear to be "moved" to the top of their containing scope, though the code itself doesn't physically move — the engine simply allocates memory for them first.
var declarations are hoisted and immediately initialized to undefined. This means accessing a var variable before its declaration line returns undefined instead of throwing an error.var is function-scoped (or globally scoped if declared outside a function). It ignores block scope entirely — a var declared inside an if or for block is accessible outside that block.var x = 5 is split into var x (hoisted, initialized to undefined) and x = 5 (stays in place).let and const are technically hoisted — the engine knows about them at the top of their block scope — but they are not initialized. They enter the Temporal Dead Zone (TDZ).ReferenceError.let and const are practically described as "not hoisted" — from a developer's perspective, you cannot use them before their declaration.let x inside a block weren't hoisted, console.log(x) before it would read x from an outer scope. Instead, it throws a ReferenceError, proving the inner let x has already "claimed" the identifier.function foo() {}). Function expressions (const foo = function() {}) and arrow functions (const foo = () => {}) follow the hoisting rules of their variable keyword (var, let, or const).var is hoisted as undefined — calling it before assignment throws TypeError: foo is not a function (because undefined is not callable).let or const is in the TDZ — calling it before assignment throws ReferenceError.let and const — they exist in the TDZ and cannot be used before their declaration.Key Interview Distinction: undefined vs ReferenceError
Accessing a var before its declaration returns undefined (hoisted and initialized). Accessing a let/const before its declaration throws ReferenceError (hoisted but in TDZ). This distinction is one of the most commonly tested hoisting questions in interviews.
Fun Fact
Hoisting was not an intentional language design — it's a side effect of how Brendan Eich implemented JavaScript's two-pass compilation in 10 days. The first pass registers all declarations, the second executes code. When ES6 added let/const, the Temporal Dead Zone was invented specifically to preserve hoisting's scoping behavior while preventing the confusing 'undefined before declaration' problem that var created.