A Closer Look at Functions
Closures
A function keeps access to the variables it was born in, even after that scope returns.
Loading visual…
Definition
A closure is a function together with references to the variables from the scope where it was created. When a function is returned from or passed out of its parent, it carries that surrounding state with it, so it can still read and update those variables long after the parent has finished running.
Examples
function makeCounter() {
let count = 0;
return () => ++count;
}
const next = makeCounter();
next(); // 1
next(); // 2The returned arrow function keeps a live reference to count, so each call updates the same private variable.
Common mistakes
- Assuming each loop iteration with `var` captures its own value; every closure shares the same variable, so they all see the final value once the loop ends.
- Forgetting that a closure keeps the whole variable alive, not just a snapshot of its value, so changes made elsewhere are visible too.
- Letting closures hold onto large data or DOM nodes longer than needed, which can keep memory from being freed.
Key takeaways
- A closure remembers the variables from the scope where it was created, not just their values at that moment.
- `var` in a loop shares one variable across every callback, so they all end up seeing the final value.
- `let` creates a fresh binding each iteration, so each callback captures its own value.
- Closures are what make private state, counters, and factories like debounce and memoize possible.
Check your understanding
What does this log? for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); }
Where you see this
- Private state in a module or hook (a value only the returned functions can touch).
- Event handlers and callbacks that remember the data they were set up with.
- Function factories like debounce, throttle, once, and memoize.