Posts
JavaScript Hoisting Explained
Hoisting is JavaScript’s setup behavior for declarations. It affects whether a function or variable can be used before the line where it appears in the file. That matters because function, var, let, and const do …
In this article
Hoisting is JavaScript’s setup behavior for declarations.
It affects whether a function or variable can be used before the line where it appears in the file. That matters because function, var, let, and const do not all behave the same way.
This post is about two questions:
- When does JavaScript create the name?
- What value, if any, is available before the declaration line runs?
Hoisting is usually explained as “JavaScript moves declarations to the top.”
That explanation is common.
It is also a little too sloppy.
JavaScript is not physically rearranging your file before it runs. A better mental model is this:
Hoisting means JavaScript sets up declarations before the code starts executing.
What happens after that depends on the kind of declaration.
Here is the clean version of the model before we get into the individual keywords.
Function Declarations Are Available Early
Function declarations can be called before they appear in the file.
sayHello();
function sayHello() {
console.log("Hello");
}
// Hello
This works because JavaScript creates the function declaration during setup.
You can think of the function name as already pointing at the function value by the time execution begins.
setup phase:
+-- sayHello -> function value
execution phase:
+-- call sayHello()
+-- run function body
That does not mean you should always write code this way. It just means the language supports it.
var Is Available Early With undefined
var declarations are also set up before execution, but they start with the value undefined.
console.log(name);
var name = "Rob";
// undefined
JavaScript knows the name label exists. The assignment has not happened yet, so the label starts attached to undefined.
This mental model is close enough for everyday use:
var name;
console.log(name);
name = "Rob";
The label is available early. The intended value is not.
That is the part people trip over.
let and const Are Different
let and const are also known during setup, but you cannot use them before execution reaches the declaration.
That gap is called the temporal dead zone.
console.log(name);
const name = "Rob";
// ReferenceError: Cannot access 'name' before initialization
This error is helpful.
It tells you the label exists in this scope, but JavaScript will not let you use it yet.
That sounds fussy until it saves you from accidentally treating an uninitialized value as real data.
The temporal dead zone is a guardrail: the name exists, but JavaScript will not let you use it before it is ready.
block scope
+-- name label exists during setup
+-- name cannot be used yet
+-- declaration line runs
+-- name gets its value
That is much easier to debug than quietly receiving undefined and accidentally building logic around a value that was not ready.
Hoisting Stays Inside Scope Boundaries
This is the important correction.
Hoisting does not move a declaration into a bigger scope.
If something is declared inside a function, it belongs to that function. If something is declared inside a block with let or const, it belongs to that block.
No promotion. No escape hatch.
Hoisting changes when a declaration is prepared. It does not change where that declaration belongs.
function exampleScope() {
var functionMessage = "inside the function";
}
console.log(functionMessage);
// ReferenceError: functionMessage is not defined
functionMessage is hoisted inside exampleScope(), not outside of it.
Same idea with a block:
if (true) {
const blockMessage = "inside the block";
}
console.log(blockMessage);
// ReferenceError: blockMessage is not defined
blockMessage belongs to the if block. Hoisting does not move it into the outer scope.
A Practical Summary
Here is the version I keep in my head:
- function declarations are available before the line where they are written
varis available early, but its value starts asundefinedletandconstare known early, but cannot be used before initialization- hoisting never moves a declaration outside its scope
That last point matters most.
Hoisting changes when a declaration is prepared.
It does not change where that declaration belongs.
Related JavaScript Posts
These topics build on each other:
- JavaScript var, let, and const Explained covers the declaration keywords hoisting behaves differently with.
- JavaScript Closures and Modified Closures Explained shows why scope boundaries matter after a function has returned.
- JavaScript this and Arrow Functions Explained separates scope lookup from
thisbinding.
-Rob