JS Fundamentals #16 — 🔒 Closures — The Magic of Remembering in JavaScript
Closures are one of the most fascinating (and sometimes confusing) concepts in JavaScript. But if you understand scope chain and lexical environment, closures become much easier to grasp. Let’s break it down with examples.
💡 What is a Closure?
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives a function access to its outer scope. In JavaScript, closures are created every time a function is created, at function creation time.
🧩 Basic Example
function outer() {
let outerVar = "I am outside!";
function inner() {
console.log(outerVar);
}
return inner;
}
const myClosure = outer();
myClosure(); // "I am outside!"
What’s happening?
outer()is called and returnsinnerfunction.innerstill has access toouterVareven thoughouter()has already finished executing.
🎯 Using Closures to Create Private Variables
function counter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const increment = counter();
increment(); // 1
increment(); // 2
increment(); // 3
Why it works
The count variable is in the lexical environment of the returned function. It is private and maintained across multiple calls.
🧰 Example: Factory Functions
function makeMultiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
Explanation
Each returned function "remembers" its own factor value, creating separate multiplier functions.
⚠️ Common Pitfall: Loops and Closures
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
Output
3
3
3
Why?
Because var is function-scoped and not block-scoped, all callbacks share the same i, which ends up as 3.
How to fix it?
Use let, which is block-scoped:
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
Correct Output
0
1
2
🌀 Returning Objects with Closures
function createPerson(name) {
let age = 0;
return {
getName: function() {
return name;
},
birthday: function() {
age++;
},
getAge: function() {
return age;
}
};
}
const person = createPerson("John");
console.log(person.getName()); // John
console.log(person.getAge()); // 0
person.birthday();
console.log(person.getAge()); // 1
Why it works
The returned object functions share access to name and age via closure.
✅ Key Takeaways
Closures allow functions to access outer scope variables even after the outer function has finished.
Useful for creating private data and factory functions.
Understand
varvsletinside loops to avoid common mistakes.