Ever wondered why some functions “remember” things even after they should’ve been forgotten?
Like:
“How does this function still know that variable?!” 🤯
Congratulations — you just met one of JavaScript’s most powerful (and confusing) features:
Closures
Let’s break it down super simply. No CS degree needed.
🎒 Think of a Function as a Person with a Backpack
When a function runs, it’s like a person going on a trip.
Before they leave, they pack a backpack with everything they might need:
- Variables from their current location
- Variables from their parent’s house (outer function)
- Even grandparents’ stuff if needed
That backpack? That’s a closure.
The function carries it everywhere, even after leaving home.
Example: Leaving Home But Keeping Memories
function parent() {
const secret = "🔑 Secret key";
function child() {
console.log(secret); // Child still has access!
}
return child;
}
const myFunction = parent();
myFunction(); // Output: 🔑 Secret key
What just happened?
-
parent()created a variable calledsecret -
parent()returned thechildfunction - Even though
parent()finished running,childstill rememberssecret!
Why? Because child packed it in its backpack (closure) before leaving home.
Real-World Example: A Counter
function createCounter() {
let count = 0; // Private variable in the backpack
return {
increment: function() {
count++;
console.log(count);
},
decrement: function() {
count--;
console.log(count);
},
getCount: function() {
return count;
}
};
}
const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.decrement(); // 1
console.log(counter.getCount()); // 1
Why is this useful?
-
countis private — you can’t access it directly - Only the functions inside the closure can modify it
- It’s like data protection built into JavaScript!
Another Way to Think About It
| Concept | Analogy |
|---|---|
| Outer function | Your home |
| Inner function | You leaving home |
| Variables | Stuff you might need |
| Closure | Your backpack |
| Returned function | You at your new location, still with your backpack |
Try This in the Console
function makeGreeting(name) {
const greeting = `Hello, ${name}!`;
return function() {
console.log(greeting);
};
}
const greetAlice = makeGreeting("Alice");
const greetBob = makeGreeting("Bob");
greetAlice(); // Hello, Alice!
greetBob(); // Hello, Bob!
Why does this work?
- Each time you call
makeGreeting(), it creates a new closure -
greetAlicehas its own backpack with “Alice” -
greetBobhas its own backpack with “Bob” - They don’t interfere with each other!
Common Pitfall: Loop Problem
This is where beginners get confused:
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
// Output: 3, 3, 3 (NOT 0, 1, 2)
Why?
- All three functions share the same
i - By the time they run,
iis already 3!
Fix #1: Use let instead of var
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
// Output: 0, 1, 2 ✅
let creates a new closure for each iteration!
Fix #2: Create a closure manually
for (var i = 0; i < 3; i++) {
(function(num) {
setTimeout(function() {
console.log(num);
}, 1000);
})(i);
}
// Output: 0, 1, 2 ✅
When Are Closures Useful?
- Private variables (like our counter example)
- Factory functions (creating multiple instances)
- Event handlers (remembering context)
- Callbacks (preserving state)
- Module pattern (organizing code)
TL;DR
| Concept | Meaning |
|---|---|
| Closure | Function’s backpack of variables |
| Outer function | Home where you pack stuff |
| Inner function | You with the backpack |
| Variables | Items in the backpack |
| Scope | What you can access |
Final Thought
Closures aren’t magic — they’re just functions remembering where they came from.
Every function in JavaScript has a built-in backpack.
It automatically packs variables from its surroundings.
That backpack goes wherever the function goes.
Once you understand that, closures stop being confusing and start being powerful.
Got questions?
Drop them in the comments! I love explaining the “why” behind the weird JavaScript behaviors we all encounter.