Closures and Higher-Order Functions
A closure is a function that remembers the variables from the scope it was created in, even after that outer scope has already finished running. This sounds abstract until you see it solve a real problem.
function makeCounter() {
let count = 0;
return function () {
count++;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
Each call to makeCounter() creates a brand new, private count variable that only the returned inner function can see or modify — count isn’t accessible from outside at all. This is how you get private state in JavaScript without needing a class, and it’s the exact mechanism behind the progress-tracking JavaScript on this very site.
A second counter is independent
const counterA = makeCounter();
const counterB = makeCounter();
console.log(counterA()); // 1
console.log(counterA()); // 2
console.log(counterB()); // 1 -- completely separate from counterA
Higher-order functions
function applyDiscount(percent) {
return function (price) {
return price * (1 - percent / 100);
};
}
const tenPercentOff = applyDiscount(10);
console.log(tenPercentOff(200)); // 180
A function that takes another function as an argument, or returns one, is called a higher-order function. You’ve already been using higher-order functions constantly without necessarily naming them that way — map, filter, reduce, and addEventListener are all higher-order functions.
Why this matters
Closures explain a lot of behavior that otherwise looks like magic — why a variable inside a setTimeout callback still has the value it had when the timeout was created, or why event handlers can each remember which specific button they belong to. Once closures click, a large amount of JavaScript stops feeling mysterious.