JavaScript Closures Explained Simply (with Real Examples)
Gaurav Aggarwal

Gaurav Aggarwal @aggarwal_gaurav_1012

About: Crafting sleek, responsive web apps with React, JavaScript, and a flair for clean code - from C++ logic to modern UI magic.

Location:
New Delhi, India
Joined:
Jun 13, 2025

JavaScript Closures Explained Simply (with Real Examples)

Publish Date: Jul 9
6 0

Ever wondered… How functions remember things… even after they've “forgotten” them.

🚀 Ever Seen JavaScript Do Magic?

Imagine if a function could remember everything it once knew — even after it’s long gone.
Sounds like wizardry? 🔮

Well, that’s not magic — that’s JavaScript Closures.

Closures are one of the most powerful, underestimated, and misunderstood features in JavaScript. Whether you’re building UI components, managing state, or writing reusable utilities — closures are quietly doing a lot of the heavy lifting.

📦 What is a Closure?

In JavaScript, a closure is created when a function “remembers” the variables from its lexical scope, even when that function is executed outside of that scope.

Example:

function outer() {
  let count = 0;

  function inner() {
    count++;
    console.log(`Count is ${count}`);
  }

  return inner;
}

const counter = outer(); // outer() runs, returns inner()
counter(); // Count is 1
counter(); // Count is 2
Enter fullscreen mode Exit fullscreen mode

Even though outer() has finished executing, the count variable is still accessible to inner() because of closure.

🧠 Why Are Closures Important?

Closures enable:

  • Data encapsulation and privacy (like private variables)
  • Callbacks with preserved state
  • Partial application / currying
  • Creating factories or modules
  • Maintaining state without using global variables

🧪 Real-World Examples of Closures

✅ 1. Private Variables (Encapsulation)

function createBankAccount() {
  let balance = 1000;

  return {
    deposit: (amount) => {
      balance += amount;
      return balance;
    },
    withdraw: (amount) => {
      if (amount > balance) return "Insufficient funds";
      balance -= amount;
      return balance;
    },
    checkBalance: () => balance
  };
}

const account = createBankAccount();
console.log(account.deposit(500));    // 1500
console.log(account.withdraw(200));   // 1300
console.log(account.checkBalance());  // 1300
console.log(account.balance);         // undefined
Enter fullscreen mode Exit fullscreen mode

Why it works: The balance variable is private, protected by a closure.

✅ 2. Function Factories (Like React Hooks or Custom Utilities)

function createGreeter(name) {
  return function (greeting) {
    console.log(`${greeting}, ${name}!`);
  };
}

const greetJohn = createGreeter("John");
greetJohn("Hello"); // Hello, John!
greetJohn("Good morning"); // Good morning, John!
Enter fullscreen mode Exit fullscreen mode

This is essentially how React’s useState or useEffect hooks maintain internal values per component instance.

✅ 3. Currying / Partial Application

function multiply(x) {
  return function (y) {
    return x * y;
  };
}

const double = multiply(2);
console.log(double(5)); // 10

const triple = multiply(3);
console.log(triple(4)); // 12
Enter fullscreen mode Exit fullscreen mode

Closures enable you to “store” the first parameter for future use.

✅ 4. Timers or Async Logic Holding State

function startCountdown(start) {
  let count = start;

  const intervalId = setInterval(() => {
    console.log(count);
    count--;

    if (count < 0) clearInterval(intervalId);
  }, 1000);
}

startCountdown(5);
Enter fullscreen mode Exit fullscreen mode

The callback in setInterval closes over count, so it can update and access it every second.

✅ 5. Loop + Closure Bug Fix

// Common mistake
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1000);
}
// Output: 3, 3, 3

// Fix using closure (or let)
for (var i = 0; i < 3; i++) {
  (function (j) {
    setTimeout(() => console.log(j), 1000);
  })(i);
}
// Output: 0, 1, 2
Enter fullscreen mode Exit fullscreen mode

Using a closure, we “capture” the value of i at each iteration.

🚧 Common Mistakes When Using Closures

  1. Memory Leaks:
    Keeping too many closures alive unnecessarily can retain memory.

  2. Unexpected Shared State:
    If you're using closures inside loops or factories, shared variables can behave unexpectedly.

🧩 Visualizing Closure

outer()
 └─ inner() ← retains access to `count` from outer scope
        ↑
   Closure keeps variables alive
Enter fullscreen mode Exit fullscreen mode

🎯 Closure in One Line

"A closure is when a function remembers its lexical environment — even after that environment is gone."

✨ Conclusion: Closures Are Everywhere

From React Hooks to debounce functions, from callback logic to async APIs — closures are the glue holding it all together.

They may feel like black magic at first, but once you understand them, you’ll see them in everything — and write cleaner, more powerful code because of it.

✍️ Want to Try This Yourself?

Here are a few prompts for readers:

  • Build a once() function — it should only allow a callback to be called once.
  • Create a timer with start/pause/reset using closure.
  • Create a simple quiz app where questions retain their state using closures.

“Understanding closures doesn’t just make you a better JavaScript developer — it makes you a smarter problem solver.” 💡


🙌 Found This Helpful?

If you enjoyed this read:

  • Clap 👏 to support the post
  • Bookmark 📌 for future reference
  • Share it with a dev buddy who’s still afraid of closures 😄

Have questions or want a follow-up post on secure local storage using closures and encryption? Drop it in the comments below.

Happy Coding!!😊

Comments 0 total

    Add comment