Module 4: Functions, Scope, and Closures
Closures and Lexical Environment
Use closures to preserve state and understand common callback patterns.
A closure occurs when a function retains access to variables from the lexical scope in which it was created, even after the outer function has returned.
function makeCounter() {
let count = 0;
return function () {
count += 1;
return count;
};
}
const counterA = makeCounter();
console.log(counterA()); // 1
console.log(counterA()); // 2
const counterB = makeCounter();
console.log(counterB()); // 1: separate state
The returned function closes over the count binding. Each call to makeCounter creates a different lexical environment, which is why the two counters do not share state.
Where closures are useful#
Closures power callbacks, event handlers, function factories, and private state patterns. They also explain why a handler can still access a variable after the code that registered it has finished running.
Watch loops and captured values#
Using let in a loop creates a per-iteration binding, which avoids a common older var closure problem:
for (let i = 0; i < 3; i += 1) {
setTimeout(() => console.log(i), 0);
}
// Logs 0, 1, 2
Closures can retain large objects longer than needed if long-lived callbacks keep references to them. Release listeners and references when they are no longer required.
Practice#
Create a makeMultiplier(factor) function that returns another function. Use it to create a double function and a triple function.