DEV Community

Muhammad Hamza
Muhammad Hamza

Posted on

JavaScript Closures – The Most Misunderstood Concept?

If you’ve been writing JavaScript for a while, you've definitely used closures—whether you realized it or not. But if someone asks:

💬 “Hey, can you explain closures in simple terms?”

Many developers hesitate. So let’s break it down, once and for all! 🚀


** What is a Closure?**

A closure is when a function "remembers" the variables from its parent scope, even after the parent has finished executing.

💡 Think of it as a backpack 🎒 that a function carries around, filled with variables from where it was created.


Example – The Classic Trap ⚠️

Guess what happens here?

function createCounter() {
  let count = 0;

  return function () {
    count++;
    console.log(count);
  };
}

const counter = createCounter();  
counter(); // 1 ✅  
counter(); // 2 ✅  
counter(); // 3 ✅  
Enter fullscreen mode Exit fullscreen mode

💡 Why does this work?

Even though createCounter() has finished executing, the inner function remembers count because of closure.

It’s as if the function "locks in" the variables it had access to at creation time.


🤔 Where Do Closures Matter in Real Life?

Data Privacy (Encapsulation)

function bankAccount(initialBalance) {
  let balance = initialBalance;

  return {
    deposit(amount) {
      balance += amount;
      console.log(`New Balance: ${balance}`);
    },
    withdraw(amount) {
      if (amount > balance) {
        console.log("Insufficient funds!");
      } else {
        balance -= amount;
        console.log(`New Balance: ${balance}`);
      }
    }
  };
}

const myAccount = bankAccount(1000);
myAccount.deposit(500);   // New Balance: 1500
myAccount.withdraw(300);  // New Balance: 1200
console.log(myAccount.balance); // ❌ Undefined (private variable)
Enter fullscreen mode Exit fullscreen mode

Here, balance isn’t directly accessible from the outside—it’s truly private! 💡

Event Listeners & Async Code

function delayedMessage(msg, delay) {
  setTimeout(() => {
    console.log(msg);
  }, delay);
}

delayedMessage("Hello after 3 seconds!", 3000);
Enter fullscreen mode Exit fullscreen mode

Even though delayedMessage finishes running, the callback inside setTimeout still has access to msg because of closure!


🚨 Common Pitfall – Closures and Loops

for (var i = 1; i <= 3; i++) {
  setTimeout(() => console.log(i), 1000);
}
Enter fullscreen mode Exit fullscreen mode

💡 You expect: 1, 2, 3

But you get: 4, 4, 4

Why? The callback inside setTimeout gets the final value of i (which is 4) when it executes.

Fix: Use let (block scope) or an IIFE

for (let i = 1; i <= 3; i++) {
  setTimeout(() => console.log(i), 1000);
}
Enter fullscreen mode Exit fullscreen mode

Now, each iteration remembers its own i thanks to closure! 🎯


** Final Takeaways**

1️⃣ Closures "remember" variables from their parent scope, even after execution finishes.

2️⃣ They help with data privacy, async behavior, and event handling.

3️⃣ Watch out for closures inside loops—use let or IIFE to avoid unexpected results.

Closures are everywhere in JavaScript. Master them, and you'll unlock better debugging skills, performance optimizations, and cleaner code.


📢 Let's Connect!

🚀 Follow me on LinkedIn for more deep dives into JavaScript and backend engineering.

🔗 Check out my GitHub for open-source projects and code samples.

📩 Have questions or want to collaborate? Reach me at imhamzaa313@gmail.com.

Top comments (0)