DEV Community

Cover image for 🔐 JavaScript Closures Explained with a Bank Account (Beginners, This One's for You!)
DHANRAJ S
DHANRAJ S

Posted on

🔐 JavaScript Closures Explained with a Bank Account (Beginners, This One's for You!)

Ever wondered how a bank keeps your balance private — so no one else can just walk in and change it?

That's exactly what a closure does in JavaScript. It locks your data away and only lets you touch it through specific actions — like a deposit or withdrawal.

Let's break it down with a real banking example. 🏦


1. What Is a Closure?

Imagine you open a bank account. The bank creates a private locker for you — your balance, your account number. No one else can access it directly. You can only interact with it through the bank's services: deposit, withdraw, check balance.

In JavaScript, a closure is when an inner function "remembers" the variables from the outer function — even after the outer function has finished running.

That's it. That's the whole idea. 🎉


2. What Does the Code Look Like?

Here's the full createAccount function we'll be working with:

function createAccount(userName, initialBalance) {
  let balance = initialBalance;    // private
  let accountNumber = Math.floor(Math.random() * 100000);

  function deposit(amount) {
    balance += amount;
    return `${userName} deposited ₹${amount}. Balance: ₹${balance}`;
  }

  function withdraw(amount) {
    if (amount > balance) {
      return "Insufficient balance";
    }
    balance -= amount;
    return `${userName} withdrew ₹${amount}. Balance: ₹${balance}`;
  }

  function getBalance() {
    return `${userName}'s balance is ₹${balance}`;
  }

  return {
    accountNumber,
    deposit,
    withdraw,
    getBalance
  };
}
Enter fullscreen mode Exit fullscreen mode

createAccount is the outer function. It holds balance and userName as private variables inside it.

deposit, withdraw, and getBalance are the inner functions. Each one is a closure — they can all see and use balance even after createAccount has finished running.


3. Why Can Inner Functions Still Access balance?

When createAccount runs and returns, you'd think balance is gone forever, right?

Nope. 🙅

JavaScript keeps balance alive in memory because the inner functions still hold a reference to it. This "remembered environment" is called the closure.

Think of it like this — even after the bank teller goes home, your locker is still safely locked inside the vault. The locker doesn't disappear. It stays as long as you still hold the keys.


4. How Does Each User Get Their Own Private Balance?

const user1 = createAccount("Ravi", 1000);
const user2 = createAccount("Anu", 5000);
const user3 = createAccount("Karthik", 200);
Enter fullscreen mode Exit fullscreen mode

Every call to createAccount creates a brand new closure — a completely separate private environment.

  • user1 gets its own balance starting at ₹1000
  • user2 gets its own balance starting at ₹5000
  • user3 gets its own balance starting at ₹200

Ravi's deposit will never affect Anu's balance. Each account is fully isolated. 🔒


5. How Do You Use the Returned Methods?

console.log(user1.getBalance());
// Ravi's balance is ₹1000

console.log(user1.deposit(500));
// Ravi deposited ₹500. Balance: ₹1500

console.log(user2.withdraw(1000));
// Anu withdrew ₹1000. Balance: ₹4000
Enter fullscreen mode Exit fullscreen mode

Every time you call user1.deposit(500), the closure remembers Ravi's balance and updates it. The next time you call user1.getBalance(), it shows the updated value.

The closure is keeping score behind the scenes — silently, reliably. 📝


6. There's a Bug in the Code — Did You Catch It?

Look at this line from the original code:

console.log(user2.getBalance)   // ❌ Missing ()
console.log(user1.getBalance()) // ✅ Correct
Enter fullscreen mode Exit fullscreen mode

user2.getBalance without () does not call the function. It just prints the function itself:

[Function: getBalance]
Enter fullscreen mode Exit fullscreen mode

Always add () when you actually want to call the function and get a result. Easy mistake — now you'll never forget! 😄


7. Why Is balance Considered "Private"?

Try accessing balance directly from outside:

console.log(user1.balance); // undefined
Enter fullscreen mode Exit fullscreen mode

It returns undefined because balance is not part of the returned object. It lives only inside the closure.

The only way to read or change it is through deposit(), withdraw(), or getBalance(). That's data privacy powered by closures — no one can sneak in and tamper with it directly.


8. The Mental Model — Picture It Like This

createAccount("Ravi", 1000)
│
├── 🔒 Private Vault (closure)
│     ├── balance = 1000
│     └── userName = "Ravi"
│
└── 🗝️ Keys Returned to You
      ├── deposit()    → updates balance
      ├── withdraw()   → updates balance
      └── getBalance() → reads balance
Enter fullscreen mode Exit fullscreen mode

The vault is the closure. The keys are the inner functions. You can only touch what's inside through the keys.


Quick Summary — 3 Golden Rules of Closures

  1. Inner functions remember outer variables — even after the outer function has finished running, the inner functions still hold on to those variables.

  2. Each function call creates its own closureuser1, user2, and user3 each have their own private balance that never leaks into each other.

  3. Closures give you data privacy — if balance isn't in the returned object, no one outside can access or modify it directly.


Closures sound scary at first — but once you picture them as a private vault with keys, they just click. 🔑

Drop a comment if you have questions. And if this made closures finally make sense, smash that ❤️ and share it with a friend who's learning JavaScript. 🚀

Top comments (0)