Mapagam
  • JavaScript 
  • Web APIs 
  • TypeScript 
  • React 

Understanding JavaScript Hoisting Without Confusion

Posted on April 19, 2025 • 4 min read • 806 words
Share via
Mapagam
Link copied to clipboard

Master JavaScript hoisting with clear examples, React use cases & real-world tips. No confusion - just deep, practical understanding.

On this page
1. What is JavaScript Hoisting? 1.1 Definition 1.2 Important Clarification 2. Variable Hoisting 2.1 var Declarations 2.2 let and const 2.3 Exercise: Predict the Output 3. Function Hoisting 3.1 Function Declarations 3.2 Function Expressions 4. Hoisting and Scope 4.1 Global Scope vs Function Scope 4.2 Block Scope 5. Real-World React Example 6. Best Practices 6.1 Always Use let or const 6.2 Declare Before You Use 6.3 Use ESLint Rules 7. Common Mistakes to Avoid Bonus Thought Prompt: 8. Summary: Key Takeaways

As a frontend developer, you’ve probably encountered strange behaviors in JavaScript where variables or functions seem to exist before they were even declared. This is often attributed to something called “hoisting.”

Understanding JavaScript hoisting isn’t just about knowing a definition; it’s about mastering control over your variables, functions, and scopes. Misunderstanding hoisting can lead to bugs that are hard to trace, especially in complex applications involving event handlers, React hooks, or asynchronous callbacks.

In this article, we’ll break down hoisting in a way that not only clears up confusion but equips you to write cleaner, more predictable JavaScript code.

Whether you’re debugging a closure inside a React modal or structuring TypeScript code for better performance, hoisting is one of those core JavaScript mechanics that keeps showing up. So let’s dive deep.

1. What is JavaScript Hoisting?

1.1 Definition

Hoisting is JavaScript’s default behavior of moving declarations to the top of their scope (global or function).

This means that even if a variable or function is defined later in the code, JavaScript handles it as if it were declared at the beginning of its scope.

1.2 Important Clarification

Hoisting only moves declarations, not initializations.

console.log(a); // undefined
var a = 10;

JavaScript hoists the declaration of a but not the assignment. So the above code is interpreted like:

var a;
console.log(a); // undefined
a = 10;

2. Variable Hoisting

2.1 var Declarations

var is function-scoped and gets hoisted to the top of the enclosing function.

function test() {
  console.log(x); // undefined
  var x = 5;
  console.log(x); // 5
}

Pitfall:

Using var can lead to unintended behavior due to its function-level scoping.

2.2 let and const

Variables declared with let and const are hoisted but remain in a Temporal Dead Zone (TDZ) until their declaration is evaluated.

console.log(y); // ReferenceError
let y = 10;

Why This Matters in React:

When dealing with state initialization or lifecycle hooks in React, mistakenly using var instead of let or const can introduce side effects that are hard to debug.

2.3 Exercise: Predict the Output

function mystery() {
  console.log(foo); // ???
  var foo = 'bar';
  return foo;
}

Try this out in a local console and explain why the result is what it is.

3. Function Hoisting

3.1 Function Declarations

Function declarations are fully hoisted with their bodies.

sayHello(); // "Hello!"

function sayHello() {
  console.log("Hello!");
}

3.2 Function Expressions

Function expressions, whether anonymous or named, are treated as variable assignments.

sayHi(); // TypeError: sayHi is not a function

var sayHi = function () {
  console.log("Hi!");
};

Here, sayHi is hoisted but only as a variable with undefined value.

TypeScript Edge Case:

If you’re using TypeScript generics to define higher-order functions, hoisting issues can become particularly confusing.

const wrap = <T>(fn: (arg: T) => void) => (arg: T) => {
  console.log("Wrapping function");
  fn(arg);
};

// Cannot hoist arrow function before this line
const wrapped = wrap((x: number) => console.log(x));

4. Hoisting and Scope

4.1 Global Scope vs Function Scope

Variables declared in the global scope are hoisted globally, whereas those inside functions are confined to function scope.

var globalVar = 'I am global';

function localScope() {
  console.log(globalVar); // "I am global"
  var localVar = 'I am local';
}

4.2 Block Scope

Block-scoped declarations (let and const) stay confined to the block.

{
  let blockScoped = true;
}

console.log(blockScoped); // ReferenceError

5. Real-World React Example

Let’s consider a React modal component that toggles visibility.

function ModalToggle() {
  var isVisible = false;

  function toggle() {
    isVisible = !isVisible;
  }

  return (
    <button onClick={toggle}>
      Toggle Modal
    </button>
  );
}

The Problem:

isVisible doesn’t persist across renders because it’s just a local var. Switching to useState would fix this, but using let or const could also help clarify scope.

function ModalToggle() {
  const [isVisible, setIsVisible] = React.useState(false);

  function toggle() {
    setIsVisible(prev => !prev);
  }

  return (
    <button onClick={toggle}>
      Toggle Modal
    </button>
  );
}

6. Best Practices

6.1 Always Use let or const

Avoid var in modern JavaScript. It introduces scope confusion and hoisting quirks.

6.2 Declare Before You Use

Even though JavaScript allows hoisting, always declare variables and functions before using them.

6.3 Use ESLint Rules

Enable ESLint rules that prevent usage of undeclared variables or hoisting-prone patterns.

7. Common Mistakes to Avoid

  • Using var and expecting block-level scope
  • Assuming function expressions are hoisted
  • Ignoring Temporal Dead Zone errors
  • Writing code assuming that initializations are hoisted

Bonus Thought Prompt:

Try rewriting a short function using all three variable declarations (var, let, const) and see how hoisting affects each one.

8. Summary: Key Takeaways

  • Hoisting moves declarations, not initializations.
  • var is function-scoped and hoisted with undefined.
  • let and const are hoisted but remain in TDZ.
  • Function declarations are hoisted, function expressions are not.
  • Avoid var and prefer let or const.
  • Always declare variables at the top of their scope.
  • Use hoisting knowledge to write more predictable React and TypeScript code.
JavaScript Hoisting   JavaScript Closures   React Hooks   TypeScript Basics   JavaScript Debugging  
JavaScript Hoisting   JavaScript Closures   React Hooks   TypeScript Basics   JavaScript Debugging  
 JavaScript Scope Explained with Simple Examples
How JavaScript Variables Actually Work: var vs let vs const 

More Reading!

  1. Beginner’s Guide to JavaScript Functions (With Best Practices)
  2. Understanding useState in React: A Beginner-Friendly Guide
  3. React useEffect Hook Explained for Beginners (With Examples)
  4. How to Use the useState Hook in React (With Simple Examples)
  5. What Is the Console API? Debugging JavaScript Easily
On this page:
1. What is JavaScript Hoisting? 1.1 Definition 1.2 Important Clarification 2. Variable Hoisting 2.1 var Declarations 2.2 let and const 2.3 Exercise: Predict the Output 3. Function Hoisting 3.1 Function Declarations 3.2 Function Expressions 4. Hoisting and Scope 4.1 Global Scope vs Function Scope 4.2 Block Scope 5. Real-World React Example 6. Best Practices 6.1 Always Use let or const 6.2 Declare Before You Use 6.3 Use ESLint Rules 7. Common Mistakes to Avoid Bonus Thought Prompt: 8. Summary: Key Takeaways
Follow me

I work on everything coding and technology

   
Mapagam
Mapagam is your go-to resource for all things related to frontend development. From the latest frameworks and libraries to tips, tutorials, and best practices, we dive deep into the ever-evolving world of web technologies.
Licensed under Creative Commons (CC BY-NC-SA 4.0).
 
Frontend
JavaScript 
Web Api 
TypeScript 
React 
Social
Linkedin 
Github 
Mapagam
Code copied to clipboard