Closures
A function in JavaScript remembers the variables of the place where it was created — even after that place has finished running. Such a function together with the variables it remembers is called a closure. Counters, settings and many library techniques are built on closures.
A function inside a function
A function can be declared inside another function and returned with return, like any value:
function makeGreeter() {
return () => 'Hello!';
}
const greet = makeGreeter();
console.log(greet()); // Hello!makeGreeter() returns a function; we store it in greet and call that.
The function remembers outer variables
An inner function sees the variables of the outer one — we know that from the scope lesson. The surprising part is that it still sees them after the outer function has finished:
function makeGreeter(name) {
return () => `Hello, ${name}!`;
}
const greetAnna = makeGreeter('Anna');
console.log(greetAnna()); // Hello, Anna!The call makeGreeter('Anna') ended long ago, but the returned function still remembers name. That is a closure.
A counter
A closure can not only read a remembered variable but also change it. This gives a counter that keeps its state between calls:
function makeCounter() {
let count = 0;
return () => {
count++;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3The variable count lives as long as the counter function exists.
Each call has its own environment
Every call of the outer function creates a new set of variables. That is why two counters do not interfere with each other:
function makeCounter() {
let count = 0;
return () => ++count;
}
const a = makeCounter();
const b = makeCounter();
a();
a();
console.log(a()); // 3
console.log(b()); // 1A function factory
A function that creates and returns other functions is called a factory. It removes repetition when you need several similar functions with different settings:
function makeMultiplier(factor) {
return (n) => n * factor;
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15Hidden data
A variable inside a closure cannot be reached from outside — only through the returned function. This protects data from accidental changes: in the counter example nobody can set count to a negative number; it can only be increased.
Common mistake
Every call of makeCounter() creates a new counter starting at zero. If you call the outer function every time, the count does not add up:
function makeCounter() {
let count = 0;
return () => ++count;
}
console.log(makeCounter()()); // 1
console.log(makeCounter()()); // 1Create the counter once, store it in a variable and call that: const counter = makeCounter();.