Hey!
Before we start — have you ever tried to understand closures and ended up more confused than before?
Yeah. Most beginners have. And it's not your fault.
The definitions are dry. The examples feel random. Nothing clicks.
So today, let's skip the textbook stuff. We'll use a real bank account to understand closures — step by step, together.
1. What Is a Closure?
Here's the simplest way to think about it.
Imagine a bank. When you open an account, the bank keeps your balance locked inside a private vault. You can't just walk in and grab it. You have to go through the bank's system — deposit, withdraw, check balance.
That vault? That's a closure in JavaScript.
A closure is when an inner function remembers the variables from its outer function — even after the outer function has stopped running.
We'll see exactly what that means in code. Keep reading.
2. Here's Our Bank Account Code
Let's look at the full example first. Don't worry if you don't get everything right away — we'll walk through it piece by piece.
function createAccount(userName, initialBalance) {
let balance = initialBalance; // private — no one can touch this directly
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 };
}
createAccount is the outer function — it's like the bank itself.
balance and userName are private variables — they live inside the bank vault.
deposit, withdraw, and getBalance are the inner functions — they're the only keys to that vault.
3. So Where Does the Closure Happen?
Here's the interesting part.
When createAccount finishes running — it's done. Gone. But look what we return:
return { accountNumber, deposit, withdraw, getBalance };
We're returning the inner functions. And those functions still remember balance and userName from inside createAccount.
That memory? That's the closure.
Think of it this way — even after a bank branch closes for the day, your account data is still safely stored inside. The branch is gone, but your balance is alive and well.
4. Let's Create Some Accounts and See It in Action
const user1 = createAccount("Ravi", 1000);
const user2 = createAccount("Anu", 5000);
const user3 = createAccount("Karthik", 200);
Each call to createAccount creates a separate closure — a separate private vault for each user.
Ravi has his own balance. Anu has hers. Karthik has his.
They don't share anything.
Now let's use them:
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
Notice — when Anu withdraws ₹1000, Ravi's balance doesn't change at all. Each closure is its own world.
5. Quick Question for You 🤔
Try guessing this before you read the answer.
What happens when you do this?
console.log(user1.balance);
Go on, take a guess.
...
The answer is undefined.
Why? Because balance is not part of the returned object. It's locked inside the closure. The only way to read it is through getBalance() — not directly.
That's data privacy, powered by closures.
6. There's a Sneaky Bug — Did You Spot It?
Look at these two lines:
console.log(user2.getBalance) // ❌ oops
console.log(user1.getBalance()) // ✅ correct
See the difference? The first one is missing ().
Without (), you're not calling the function — you're just pointing at it. JavaScript will print this instead:
[Function: getBalance]
Always use () when you want to actually run a function. Small thing — big difference.
7. The Mental Model — Vault + Keys 🗝️
Here's a picture to lock this in:
createAccount("Ravi", 1000)
│
├── 🔒 Private Vault (the closure)
│ ├── balance = 1000
│ └── userName = "Ravi"
│
└── 🗝️ Your Keys (returned methods)
├── deposit() → adds to balance
├── withdraw() → subtracts from balance
└── getBalance() → reads the balance
You hold the keys. But the vault stays hidden.
No one else can open it. No one else can change the balance — unless they go through the keys you have.
8. One More Thing — Try This Yourself!
Open your browser console or a JS playground and run this:
const user3 = createAccount("Karthik", 200);
console.log(user3.withdraw(500));
// What do you think this prints?
Take a guess before you run it.
...
It prints "Insufficient balance" — because Karthik only has ₹200 and tried to withdraw ₹500.
The withdraw function checked balance through the closure and protected the account. 💪
Quick Summary — 3 Things to Remember
A closure remembers — inner functions keep access to outer variables even after the outer function is done.
Each call is its own world — every
createAccountcall creates a fresh, isolated closure. Ravi and Anu never share data.Closures = privacy —
balanceisn't exposed directly. You can only interact with it through the functions returned. That's intentional and powerful.
Thanks for reading! Keep building, keep asking questions. ✨
Top comments (0)