© 2026 WriterDock.

Coding

JavaScript Hoisting Explained with Real Code Examples

Suraj - Writer Dock

Suraj - Writer Dock

January 21, 2026

JavaScript Hoisting Explained with Real Code Examples

If you have ever tried to use a variable before you declared it, you might have expected an error. In most programming languages, that is exactly what happens. But in JavaScript, you might get undefined, or the code might work perfectly fine.

Welcome to the weird and wonderful world of Hoisting.

Hoisting is one of the most misunderstood concepts in JavaScript. It is a frequent topic in technical interviews and a common source of bugs for developers who are used to languages like Java or C++.

In this guide, we will break down exactly what hoisting is, how the JavaScript engine handles your code behind the scenes, and how var, let, const, and functions behave differently.

What is Hoisting?

In simple terms, Hoisting is a behavior where variable and function declarations appear to be "moved" to the top of their scope before the code is executed.

It allows you to use functions and variables before you have technically written them in your code file.

However, the code isn't actually physically moved. Your text editor doesn't rearrange your lines. instead, this happens inside the JavaScript engine during the compilation phase.

To understand hoisting, you need to understand the two phases of running JavaScript code:

  1. The Creation Phase (Compilation): The engine scans your code for variables and functions and sets up memory space for them.
  2. The Execution Phase: The engine runs your code line-by-line, assigning values and executing functions.

Hoisting happens during that first phase.

Variable Hoisting: The var Keyword

Before ES6 (modern JavaScript), var was the only way to declare variables. Its hoisting behavior is unique and often confusing.

When the JavaScript engine sees a var declaration, it hoists the declaration to the top, but it does not hoist the assignment. It initializes the variable with a default value of undefined.

Example 1: Accessing var Before Declaration

javascript
1console.log(myAge); // Output: undefined
2var myAge = 30;
3console.log(myAge); // Output: 30

What is happening here? You might expect the first line to throw a ReferenceError because myAge hasn't been defined yet. Instead, it prints undefined.

Here is how the JavaScript engine interprets that code:

1// Phase 1: Creation (Hoisting)
2var myAge; // Declared and set to undefined
3
4// Phase 2: Execution
5console.log(myAge); // It exists, but has no value yet
6myAge = 30;         // Now it has a value
7console.log(myAge);

The declaration (var myAge) was hoisted. The assignment (= 30) stayed exactly where it was. This leads to the classic bug where a variable exists but doesn't have the data you expect yet.

The Temporal Dead Zone: let and const

When ES6 introduced let and const, it aimed to fix the weird behavior of var.

Variables declared with let and const are still hoisted. However, there is a critical difference: they are not initialized with undefined.

They remain in a state called the Temporal Dead Zone (TDZ) until the execution reaches the line where you declared them.

Example 2: The Crash

1console.log(myName); // ReferenceError: Cannot access 'myName' before initialization
2let myName = "Alice";

Even though the engine knows myName exists (because it scanned the code), it refuses to let you touch it until the variable is fully initialized. This is a safety feature. It prevents you from accidentally using empty variables.

Visualizing the Temporal Dead Zone

Think of the TDZ as a time window.

  • Start of Scope: The variable enters the TDZ.
  • TDZ: If you try to access the variable here -> Error.
  • Declaration Line: The variable is initialized. TDZ ends.
  • Rest of Scope: You can use the variable safely.

Function Hoisting: The Good and The Bad

Hoisting behaves very differently depending on how you define your functions. This is where many developers get tripped up.

1. Function Declarations

A standard function declaration is fully hoisted. This means the name and the function body are moved to the top.

You can call the function before you write it.

1greet(); // Output: "Hello, World!"
2
3function greet() {
4  console.log("Hello, World!");
5}

This is actually a useful feature. It allows you to put your main logic at the top of the file and hide the helper implementation details at the bottom, making your code easier to read.

2. Function Expressions

If you assign a function to a variable (a Function Expression), hoisting follows the rules of the variable keyword you used (var, let, or const).

1sayHi(); // TypeError: sayHi is not a function
2
3var sayHi = function() {
4  console.log("Hi!");
5};

Why did this fail? Because we used var, the variable sayHi was hoisted and set to undefined. When you tried to run sayHi(), you were essentially trying to run undefined(). Since undefined is not a function, JavaScript throws a TypeError.

If you used let or const instead of var, you would get a ReferenceError (due to the Temporal Dead Zone) instead of a TypeError.

3. Arrow Functions

Arrow functions behave exactly like Function Expressions. They are variables.

1calc(); // ReferenceError
2
3const calc = () => {
4  console.log("Calculating...");
5};

Since calc is a const variable, it stays in the Temporal Dead Zone until the code reaches the definition.

Hoisting Precedence: Who Wins?

What happens if you declare a variable and a function with the same name? The JavaScript engine has a specific hierarchy for resolving these conflicts during the creation phase.

Functions are hoisted before variables.

1console.log(typeof score); // Output: "function"
2
3var score = 10;
4
5function score() {
6  return 50;
7}
8
9console.log(typeof score); // Output: "number"

Breakdown:

  1. Creation Phase: The engine sees the function score. It hoists it. Then it sees var score. It ignores the variable declaration because score is already declared as a function.
  2. Execution Phase: Line 1 prints "function". Then, line 3 executes score = 10. The value is overwritten.
  3. Line 9 prints "number".

This is a confusing pattern that you should avoid in production code, but it illustrates how aggressive function hoisting is.

Best Practices to Avoid Hoisting Bugs

Understanding hoisting is great for debugging, but in your daily coding, you generally want to avoid relying on it (except for function declarations).

1. Declare Everything at the Top

Historically, developers were taught to declare all variables at the very top of the function to mimic what the engine does.

1function processData() {
2  var data, index, result; // Manual hoisting
3  // ... rest of code
4}

While this clarifies scope, it is messy.

2. Use const and let exclusively

Stop using var. The behavior of var (hoisting with undefined) creates silent bugs. The behavior of const and let (hoisting with Error) creates loud bugs.

Loud bugs are better. They force you to fix the problem immediately rather than letting null data seep into your application logic.

3. Initialize Variables When You Declare Them

Don't just write let user;. Write let user = null; or let user = {};. This makes your intent clear and ensures the variable has a valid type from the moment it is usable.

4. Enable Strict Mode

Using use strict"; at the top of your file or function prevents you from accidentally creating global variables, which often interact poorly with hoisting mechanics.

FAQ: Common Hoisting Questions

Q: Are classes hoisted in JavaScript? A: Classes are hoisted, but they behave like let and const. They remain in the Temporal Dead Zone. You cannot instantiate a class before the line where you defined it.

1const p = new Person(); // ReferenceError
2class Person {}

Q: Does hoisting work the same in strict mode? A: Yes, the mechanics of hoisting are the same. However, strict mode prevents other related bad behaviors, like undeclared variables automatically becoming global.

Q: Why does JavaScript even have hoisting? A: It wasn't necessarily a "feature" designed for utility. It was a byproduct of how the early JavaScript interpreters worked. They needed to do a pass over the code to allocate memory before running it. However, function hoisting specifically was kept because it allows for "mutual recursion" (two functions calling each other) and cleaner code organization.

Conclusion

Hoisting is not magic. It is simply the difference between "allocating memory" and "executing code."

  • var: Hoisted and initialized as undefined. (Avoid using this).
  • let / const: Hoisted but stay in the Temporal Dead Zone. (Use these).
  • Function Declarations: Fully hoisted. (Safe to use anywhere).
  • Function Expressions / Arrow Functions: Not hoisted as functions; they follow variable rules.

By understanding these rules, you can stop guessing why your variable is undefined and start writing predictable, robust JavaScript. The next time you see a ReferenceError, don't panic—just remember the Temporal Dead Zone.

About the Author

Suraj - Writer Dock

Suraj - Writer Dock

Passionate writer and developer sharing insights on the latest tech trends. loves building clean, accessible web applications.