JavaScript hoisting is one of the most misunderstood behaviors in JavaScript. It allows variables and functions to be used before they are declared, but this can lead to confusing bugs if not fully understood.
Have you ever wondered why JavaScript lets you use a variable before you declare it? Or why some functions seem to “magically” work even when they’re called first?
This behavior is due to JavaScript hoisting, a core concept that often confuses beginners.
Welcome to the fascinating world of hoisting in JavaScript. It’s one of the most common sources of confusion for beginners—and even experienced developers—because it’s not immediately visible in the code.
In this deep-dive post, we’ll explore:
What hoisting really means
How it behaves with
var,let, andconstFunction declarations vs expressions
Temporal Dead Zone (TDZ)
Internal mechanics of the JavaScript engine
Real-world bugs caused by hoisting
Best practices to write clean, predictable code
Let’s get started. 🎯
🔍 What is Hoisting?
JavaScript hoisting is the behavior where variable and function declarations are moved to the top of their scope during compilation.
Hoisting is a behavior in JavaScript where declarations are moved to the top of their scope during the compilation phase, before the code is executed.
But there’s a catch:
Only declarations are hoisted — not initializations.
This means JavaScript sets aside space in memory for your variables and functions before actually running the code. That’s why you can reference some variables or functions before they appear in your code — though not always safely.
📦 JavaScript Hoisting with var, let, and const
Let’s break it down by keyword.
1. var – The Classic Culprit
This works because:
The
var name;declaration is hoistedThe assignment
name = "Alice"stays in placeSo at the time of
console.log,nameexists but isundefined
Because of JavaScript hoisting, var is lifted to the top and initialized as undefined, allowing early access before the declaration line.
Behind the scenes, the engine sees:
2. let and const – The Safer Modern Way
These throw ReferenceErrors, even though they’re hoisted too!
So what’s going on?
🕳️ Temporal Dead Zone and JavaScript Hoisting
The Temporal Dead Zone is the period between entering the block scope and the point where the variable is declared.
Understanding how let and const behave during JavaScript hoisting helps you avoid the Temporal Dead Zone (TDZ) and related errors.
During this time:
The variable exists
But you can’t access it — doing so will throw an error
This helps prevent bugs and makes code behavior more predictable — a major improvement over var.
⚙️ Function Hoisting – Declarations vs Expressions
1. Function Declarations – Fully Hoisted
Function declarations are fully hoisted — both name and body — so you can use them before they’re defined.
2. Function Expressions – Partially Hoisted
Why TypeError? Because sayHi is hoisted as a variable (with undefined), not a function. So you’re calling undefined().
3. Arrow Functions – Same as Expressions
Just like function expressions, arrow functions are not hoisted as functions.
🧬 Behind the Scenes – What the Engine Actually Does
When JavaScript code is run, two phases happen:
1. Compilation Phase (before execution)
The engine scans the code
Allocates memory for all declarations
varvariables: initialized toundefinedlet/const: hoisted but uninitialized (TDZ begins)Function declarations: hoisted with body
2. Execution Phase
Code runs line by line
Assignments happen
Any usage before declaration for
let/constthrows an error
💥 Common Bugs Caused by JavaScript Hoisting
Here’s a real-world example of how misunderstanding JavaScript hoisting can introduce subtle and hard-to-debug issues.
Bug:
You might expect message to be undefined when age < 18, but due to var hoisting, the variable is declared at the top of the function.
So internally, it becomes:
Fix:
Use let to limit scope:
✅ Summary Table: Hoisting Behavior
| Keyword/Type | Hoisted? | Initialized? | Accessible Before Declaration? | TDZ Applies? |
|---|---|---|---|---|
var | Yes | Yes (undefined) | ✅ Yes (but undefined) | ❌ No |
let | Yes | No | ❌ ReferenceError | ✅ Yes |
const | Yes | No | ❌ ReferenceError | ✅ Yes |
| Function Declaration | Yes | Yes (full body) | ✅ Yes | ❌ No |
| Function Expression | Yes (var) | No | ❌ TypeError / ReferenceError | ✅ Yes |
| Arrow Function | Yes (const/let) | No | ❌ TypeError / ReferenceError | ✅ Yes |
🛡️ Best Practices to Avoid Hoisting Bugs
Always declare variables at the top of their scope
Use
letorconstinstead ofvarDon’t use variables before declaring them—even if they’re hoisted
Prefer function declarations only when you must call a function early
Turn on
'use strict';to catch silent hoisting errors
🧘 Final Thoughts
Understanding hoisting helps you write better, safer JavaScript. It’s not just a quirky language feature — it’s how the engine works under the hood. And once you internalize it, you’ll stop being surprised by weird bugs.
Mastering JavaScript hoisting is essential for writing predictable and bug-free code, especially when working with variables and functions across different scopes.
Think of hoisting like packing your backpack before school — JS packs all your variables and functions first, then starts the school day (execution)!
✨ Bonus Tip: Use ESLint
Tools like ESLint can catch hoisting-related issues early. For example, rules like no-use-before-define can warn you when you’re using variables too early.
