Hoisting in JavaScript: The Hidden Mechanism Every Developer Must Understand

Ever seen JavaScript call a function at the very top while its definition sits at the bottom—yet it still runs? Or accessed a variable before declaration and got undefined instead of an error? That’s hoisting. Understanding it not only helps with technical interviews but is also key to how the JavaScript engine works under the hood—before any line of code executes.

hoistingJavaScriptvarletconstfunction
Cover image: Hoisting in JavaScript: The Hidden Mechanism Every Developer Must Understand
Avatar of Trung Vũ Hoàng

Trung Vũ Hoàng

Author

29/3/20265 min read

1. What Exactly Is Hoisting?

Many resources describe hoisting as “moving declarations to the top of the file.” It’s an easy way to think about it, but not entirely accurate—no lines of code are actually moved. What really happens is: during the Creation Phase, the JavaScript engine scans the source, finds all variable and function declarations, and allocates memory for them—before execution starts.

This creates the “illusion” that every declaration is available from the start of its scope. Without a clear understanding of this, you can run into subtle logic bugs—especially in multi-contributor codebases with mixed coding styles.

2. var, let, const — How Does Hoisting Behave Differently?

Here’s the key distinction:

var — Hoisted and initialized to undefined

When you declare with var, the variable is hoisted and assigned the default value undefined from the start. You can access it before the declaration line without a crash—but you’ll get undefined, which can be confusing.

let and const — Hoisted but in the Temporal Dead Zone (TDZ)

let and const (ES6+) are also hoisted, but they are not initialized. They sit in the Temporal Dead Zone (TDZ)—the period from when the block starts until the declaration line executes. Accessing a variable during this period throws a ReferenceError immediately.

This is why let and const are considered “safer”—they force you to declare before use, making code clearer and more readable.

3. Hoisting with Functions: Function Declaration vs Expression

Function Declaration — Fully hoisted

Functions declared with the traditional syntax (function functionName() {}) are hoisted entirely—both the name and the function body are allocated up front. You can call the function anywhere in the scope, even before its definition line.

Function Expression — Follows the variable’s rules

If you assign a function to a variable (const fn = function() {} or var fn = function() {}), the variable’s hoisting rules apply. With var, the variable is undefined, and calling it early yields “is not a function”. With const, you’ll get a ReferenceError.

4. Why Does JavaScript Have This Mechanism?

Hoisting isn’t an accidental quirk—it has a clear design rationale. Most importantly, it enables mutual recursion. If function A calls function B and B calls A, both must “know” about each other’s existence. Hoisting allows that regardless of declaration order in the file.

It also reflects JavaScript’s original philosophy: try to execute rather than fail immediately on small issues. In modern development, however, we prefer discipline and explicitness over this kind of error-prone “flexibility.”

5. Real-World Examples and Pitfalls

console.log(name); // undefined — not an error!
var name = "JavaScript";
console.log(name); // "JavaScript"

The engine effectively does this: var name; is hoisted first (with the default value undefined), then the first console.log runs and sees undefined, and only afterward does the assignment execute. Replacing var with let triggers a ReferenceError on the first line.

Another common trap: var has no block scope. A variable declared with var inside an if or for “leaks” out of the block and exists at function or global scope—fuel for countless bugs in asynchronous loops.

6. Best Practices — Work Well with Hoisting

  • Forget var; use let and const: This is rule number one. Use const when you don’t need reassignment, and let for the rest.

  • Declare variables at the top of the scope: Even with let/const, declaring variables near the top of a function helps readers grasp all the “ingredients” of the logic.

  • Avoid naming variables the same as functions: This is a source of confusing, hard-to-reproduce bugs.

  • Enable Strict Mode: Add "use strict"; at the top of the file to prevent JavaScript from implicitly creating globals when you forget a declaration keyword.

Connection to Closures: Hoisting prepares the Lexical Environment—the context a Closure later “captures” and remembers. Understanding hoisting is a natural stepping stone to deeply understanding closures.

7. Summary

Hoisting isn’t a design flaw—it’s an intentional feature. Once you understand the Creation Phase and Execution Phase, you grasp how JavaScript actually operates—beyond surface-level explanations.

Clean code isn’t about clever tricks. It’s code any teammate can read and understand immediately. Use your knowledge of hoisting to write clearer code—not to hide complexity.

Found this article helpful?

Contact us for a free consultation about our services

Contact us

Bài viết liên quan