Let’s be honest.
Closures in JavaScript sound scary at first.
You’ve probably seen definitions like:
“A closure is a function that retains access to its lexical scope…”
And your brain immediately goes: Nope.
But here’s the truth — closures are not complicated. You’re already using them. You just don’t realize it yet.
So let’s break it down slowly, clearly, and in a way that actually sticks.
First — don’t scroll
Look at this code.
function outer() {
let name = "Alex";
return function inner() {
console.log(name);
};
}
const fn = outer();
fn();
What do you think the output is?
Don’t skip. Think.
.
.
.
Now imagine you bet money on your answer.
Output:
Alex
If that surprised you even a little… good. You’re in the right place.
Now here’s the real question
Why is name still available?
outer() is already done. Gone. Finished.
So why didn’t name disappear with it?
If your answer is:
“Because JavaScript is weird”
No. That’s just avoiding the truth.
Or worse — memorizing without understanding.
This is where closures hit you
A closure means:
A function remembers variables from where it was created — not where it runs.
Read that again.
Not where it runs.
Where it was created.
If that still feels abstract, don’t worry — I’m going to corner you with examples.
Still not convinced? Let’s push you
Real scenario: Login system
function createLogin(username, password) {
return function (inputPassword) {
if (inputPassword === password) {
console.log(`Welcome ${username}`);
} else {
console.log("Wrong password");
}
};
}
const login = createLogin("Sam", "pass123");
login("pass123");
login("123");
Output:
Welcome Sam
Wrong password
Now answer this:
Where is password stored?
- Not global
- Not inside
login - Not accessible anywhere
Yet it still works.
And no, it’s not “saved somewhere magically.”
That’s closure.
And if that didn’t click yet, don’t worry — I’m not done with you.
Let’s corner you a bit
Try this:
console.log(login.password);
Before you run it — predict it.
Output:
undefined
So…
- You can’t access it
- But the function can
Sounds like magic?
It’s not magic. It’s memory + scope working together.
Real-world situation: You click a button
Let’s say you're tracking clicks.
function createClickTracker() {
let count = 0;
return function () {
count++;
console.log(`Clicked ${count} times`);
};
}
const click = createClickTracker();
click();
click();
click();
Output:
Clicked 1 times
Clicked 2 times
Clicked 3 times
Now don’t just read.
Answer this:
Why is count not resetting to 0?
Pause. Actually think.
Because you're not creating count again.
You're reusing the same closed-over variable.
Let’s make it uncomfortable
What if I do this:
const click1 = createClickTracker();
const click2 = createClickTracker();
click1();
click1();
click2();
Before you scroll — predict it.
Output:
Clicked 1 times
Clicked 2 times
Clicked 1 times
Now think carefully.
If closures were “shared memory”… this wouldn’t happen.
Each function has its own private world.
Yes — like separate tabs in your browser.
Real app logic: Shopping cart
function createCart() {
let items = [];
return {
add(item) {
items.push(item);
console.log(`${item} added`);
},
view() {
console.log(items);
}
};
}
const cart = createCart();
cart.add("Laptop");
cart.add("Phone");
cart.view();
Output:
Laptop added
Phone added
["Laptop", "Phone"]
Now I dare you:
console.log(cart.items);
Before running it — what do you expect?
Output:
undefined
So let me ask you:
If you can’t access items, how is it still updating?
Because it lives inside a closure.
Not global. Not exposed. Not reachable.
Hidden — but alive.
Now let’s catch a bug (you’ve probably made this)
for (var i = 1; i <= 3; i++) {
setTimeout(function () {
console.log(`Order ${i}`);
}, 1000);
}
What do you expect?
Be honest.
Order 1
Order 2
Order 3
What actually happens?
Order 4
Order 4
Order 4
Yeah. That bug.
Don’t say “I wouldn’t make that mistake.”
You either already have — or you will.
Fix it using closure
for (var i = 1; i <= 3; i++) {
(function (order) {
setTimeout(function () {
console.log(`Order ${order}`);
}, 1000);
})(i);
}
Output:
Order 1
Order 2
Order 3
Each loop creates a new memory.
Same code pattern. Different outcome.
That’s the twist closures bring.
Or use modern JS (yes, this is easier)
for (let i = 1; i <= 3; i++) {
setTimeout(function () {
console.log(`Order ${i}`);
}, 1000);
}
Same correct output.
Because let creates a new scope every time.
Different keyword. Completely different behavior.
Pause. Don’t scroll yet.
If I ask you now:
What is a closure?
Don’t repeat a definition.
Say it like a developer.
Say it like you actually understand it.
The version you should remember
A closure is when a function carries its data with it, even after the original function is gone.
Not stored globally.
Not recreated.
Carried.
Quick self-check (no cheating)
What will this print?
function createDiscount(discount) {
return function (price) {
return price - price * discount;
};
}
const discount10 = createDiscount(0.1);
console.log(discount10(200));
Don’t rush. Think.
.
.
.
Output:
180
If you got that right, you’re not guessing anymore.
If you didn’t — good. That means you’re actually learning instead of memorizing.
Final reality check
Closures are not some “advanced topic.”
They are used in:
- React hooks
- Event listeners
- API handlers
- Timers
- State management
So if you skip this…
You’re not skipping theory.
You’re skipping how JavaScript actually works.
One last push
Next time you see:
- Function inside function
- Returned function
- Hidden variables
- Values that “don’t reset”
Don’t ignore it.
Stop and ask:
“What is this function remembering?”
That question alone will level you up.
And now you actually understand closures — not just pretend to.
Top comments (0)