What is Hoisting?
Hoisting is the name for a JavaScript behavior where variable and function declarations act as if they were moved to the top of their containing scope. Nothing is physically moved: the engine registers every declaration in a scope before it runs any code in that scope, and the rest of this post is consequences of that one fact. This means that you can use variables and functions before they are declared in the code. Understanding hoisting is crucial for mastering JavaScript, as it can lead to unexpected results if not properly understood.
How Hoisting Works
In JavaScript, both variable declarations (using var, let, or const) and function declarations are hoisted. However, there are important differences in how they behave.
Variable Hoisting
When you declare a variable using var, the declaration is hoisted to the top of its function or global scope, but the assignment remains in place. This can lead to situations where a variable is undefined if accessed before its assignment.
console.log(myVar); // returns undefined
var myVar = 5;
console.log(myVar); // returns 5
In the example above, the first console.log outputs undefined because the declaration of myVar is hoisted, but the assignment myVar = 5 is not. The code is interpreted as:
var myVar;
console.log(myVar); // returns undefined
myVar = 5;
console.log(myVar); // returns 5
Function Hoisting
Function declarations are fully hoisted, meaning you can call a function before it is defined in the code. This is because the entire function declaration is hoisted to the top of its scope.
greet(); // returns "Hello, World!"
function greet() {
console.log("Hello, World!");
}
In this case, the function greet can be called before its declaration because the entire function is hoisted to the top.
Let and Const Hoisting
Unlike var, variables declared with let and const are also hoisted, but they are not initialized. Accessing them before their declaration results in a ReferenceError.
// returns ReferenceError: Cannot access 'myLet' before initialization
console.log(myLet);
let myLet = 10;
// returns ReferenceError: Cannot access 'myConst' before initialization
console.log(myConst);
const myConst = 20;
In this example, both myLet and myConst throw a ReferenceError because they are in a "temporal dead zone" from the start of the block until their declaration.
Implications of Hoisting
Understanding hoisting is essential for avoiding common pitfalls in JavaScript. Here are a few key takeaways:
- Always Declare Variables: To avoid confusion, always declare your variables at the top of their scope.
- Function Declarations vs. Expressions: Remember that function declarations are hoisted with their body, but for a function expression (including arrow functions) only the variable is hoisted, not the function assigned to it.
// returns TypeError: square is not a function
console.log(square(5));
var square = function (num) {
return num * num;
};
In this case, the square variable is hoisted, but since it is a function expression, it is not initialized until the assignment is reached, leading to a TypeError.
Wrapping Up
If you are wondering why the functions with function declaration are hoisted and why var func = function() {} or func() => {} are not hoisted, you can check out this post where I have discussed how JS execution engine reserves memory for the code. In short, if declaration is made using the function keyword, the engine stores entire chunk of that function definition along with the reference which allows us to call the function before definition at runtime while in other cases, memory is initially undefined until execution context reaches its definition.
Where this stops applying
In modern code hoisting rarely bites. With let, const, and ES modules, using a name too early throws immediately instead of quietly giving you undefined. The confusing cases above are mostly var cases, and most codebases have stopped writing var.
"Moved to the top" is a teaching model. The specification never moves code. It describes environment records that are filled in before execution starts. The model predicts the right output, but it will mislead you if you take it literally, for example about where a let variable "is" during its dead zone.
Classes behave like let. A class declaration is hoisted but uninitialized, so using it before its definition is a ReferenceError. This post does not cover that case.
The rule that makes all of this a non-issue: declare before use, prefer const, and let a linter flag the rest.
✌️ Stay curious, Keep coding, Peace nerds!
Updated 9 October 2026: reworded the definition to match the specification, fixed a dead link, added sources, and added the section on where this stops applying.
Top comments (0)