A closure is a function that remembers the variables from the place where it was created, even after that place has finished running.
That sounds odd, so here is a real example. Imagine a counter that keeps its own count.
function makeCounter() {
let count = 0;
return function () {
count++;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // → 1
console.log(counter()); // → 2
console.log(counter()); // → 3
What is going on?
makeCounter()creates a variablecountand returns an inner function.makeCounterhas finished. Normallycountwould disappear.- But the inner function still refers to
count, so JavaScript keeps it alive for that function.
The inner function “closed over” count. Nobody else can reach it: there is no way to set count except by calling counter(). That makes closures a way to create private data.
Each closure has its own copy
const a = makeCounter();
const b = makeCounter();
console.log(a(), a(), b()); // → 1 2 1
function makeCounter() {
let count = 0;
return () => ++count;
}
a and b do not interfere. Each call to makeCounter() creates a fresh count.
A practical use: remembering a setting
function makeGreeter(greeting) {
return function (name) {
return `${greeting}, ${name}!`;
};
}
const sayHello = makeGreeter("Hello");
const sayHola = makeGreeter("Hola");
console.log(sayHello("Maya")); // → Hello, Maya!
console.log(sayHola("Maya")); // → Hola, Maya!
Closures in event handlers
Closures are why a click handler can use variables from where it was created:
<button id="add">Add</button>
<p id="total">0</p>
<script>
let total = 0; // lives on after this code finishes
document.querySelector("#add").addEventListener("click", () => {
total += 1; // the handler remembers `total`
document.querySelector("#total").textContent = total;
});
</script>
Closures and loops
The classic var bug from var and Hoisting is a closure problem: all callbacks shared one i. With let, each round creates a new variable, so each callback gets its own.