Hoisting
Introduction
Hoisting is the name for JavaScript's behavior of processing declarations before executing code. It makes some names available before their source line appears — but different declaration kinds behave very differently.
The most important correction: hoisting does not mean JavaScript physically moves your code. It means bindings are created during the memory phase.
Why This Matters
Hoisting is one of the highest-frequency JavaScript interview topics because it combines execution contexts, declaration types, TDZ, and function calls. A strong answer is precise: var is hoisted to undefined, function declarations are hoisted as callable functions, and let/const/class are hoisted but blocked by TDZ.
Theory
Hoisting by declaration kind
| Source form | What is hoisted? | Early read/call result |
|---|---|---|
var x | Binding initialized to undefined | undefined. |
function f() {} | Binding initialized to function object | Callable. |
let x | Binding created but uninitialized | ReferenceError in TDZ. |
const x | Binding created but uninitialized | ReferenceError in TDZ. |
class C {} | Binding created but uninitialized | ReferenceError in TDZ. |
var f = function () {} | Only f binding initialized to undefined | Calling gives TypeError. |
const f = () => {} | f binding in TDZ | Reading/calling gives ReferenceError. |
Assignments do not hoist
console.log(x); var x = 5; does not become var x = 5; console.log(x);. The actual mental rewrite is closer to var x; console.log(x); x = 5;.
Function declarations vs expressions
A function declaration is callable before it appears because the memory phase creates the function object. A function expression is just a value produced during execution. The variable may be hoisted, but the value is not assigned until the execution phase reaches the assignment.
let and const are not unhoisted
If let were truly not hoisted, an inner let would not affect code before its declaration. But it does: it shadows the outer name from the beginning of the block and throws in the TDZ. That proves the binding exists early.
Practical advice
Do not rely on hoisting for readability. Put declarations near the top of the scope, prefer const, use function declarations intentionally, and avoid mixing var with modern lexical declarations.
Visual Diagrams
Original source
console.log(x)
var x = 5
Mental model
Creation: x -> undefined
Execution: console.log(x) -> undefined
x = 5
Not true
var x = 5 moved above console.logOnly the declaration binding is prepared early; the assignment stays in place.
Most available before line function declaration -> callable var declaration -> readable as undefined let / const / class -> binding exists but TDZ throws Least available before line
Hoisting is not one behavior; it depends on the declaration kind.
Code Examples
Function declaration vs function expression
The declaration is callable early. The expression assigned to var is not.
Hoisting does not hoist the assignment
The first read sees the creation-phase value, not the later assigned value.
Classes have a temporal dead zone
Class declarations are hoisted as bindings, but they cannot be used before initialization.
Playground
Press Run to execute the code and see output here.
Output Prediction
Predict the output #1
1console.log(item);2var item = 'book';3console.log(item);Predict the output #2
1console.log(typeof getName);2console.log(typeof getAge);3 4function getName() {5 return 'Ada';6}7 8var getAge = function () {9 return 42;10};Predict the output #3
1foo();2 3function foo() {4 console.log('A');5}6 7foo = function () {8 console.log('B');9};10 11foo();Predict the output #4
1try {2 console.log(total);3} catch (error) {4 console.log(error.name);5}6 7let total = 1;8console.log(total);Coding Exercises
Refactor a hoisting trap
MediumRewrite reportScore so it has no hoisting surprises. It should return 'missing' when the input is null or undefined; otherwise it should return 'score: X'. Use const/let and declare values before reading them.
Interview Questions
1What is hoisting in JavaScript?
Hoisting is the observable result of the creation phase of an execution context. Declarations are processed before statements execute. var bindings are initialized to undefined; function declarations are initialized to function objects; let, const, and class bindings are created but remain uninitialized in the temporal dead zone. Assignments are not hoisted.
Follow-ups
- Are let and const hoisted?
- Why does a function expression assigned to var throw TypeError when called early?
2Is JavaScript moving declarations to the top of the file?
No. That is a teaching shortcut, not the real model. The engine creates bindings during the creation phase, then executes code in its original order. This distinction matters because assignments, function expressions, and lexical initializations still happen where they appear.
Quiz
1. What does `console.log(x); var x = 5;` print first?
2. What happens when you call a `var` function expression before assignment?
3. Which declaration has a TDZ?
Summary
- Hoisting means declarations are processed during creation before execution begins.
- `var` is hoisted with value `undefined`; function declarations are hoisted as callable function objects.
- `let`, `const`, and `class` are hoisted but inaccessible during the temporal dead zone.
- Assignments and function-expression values are not hoisted.
Cheat Sheet
Hoisting truth: bindings are prepared early; code is not physically moved.
var x -> early value undefined
function f() {} -> early callable function
let / const / class -> TDZ until declaration executes
var f = function () {} -> f is undefined until assignment
Avoid traps: declare before use; prefer const; explain creation phase, not code movement.