Build fluency in the vocabulary of a returned function still reaching into its outer scope's variables.
0 / 5 completed
1 / 5
At standup, a dev mentions a function returned from another function that still has access to variables from the outer function's scope, even after the outer function has already finished running and returned. What is this behavior called?
A closure is exactly this: a function that retains access to variables from its outer function's scope even after that outer function has already finished executing and returned, because the returned function keeps a live reference to that enclosing scope rather than a one-time snapshot. Memoization is an unrelated technique about caching a function's return value by its arguments. This lingering access to an outer scope's variables, long after that scope would otherwise be discarded, is exactly what makes a closure useful for things like a counter function that remembers its own running total between calls.
2 / 5
During a design review, the team relies on a closure specifically so a returned function can keep incrementing a private counter variable that nothing outside the closure can directly access or modify. Which capability does the closure provide here?
The closure provides encapsulated, persistent private state, since the counter variable lives only inside the closure's retained outer scope, with no way for anything outside the returned function to reach in and directly read or modify it except by calling that function itself. A counter declared as a fully public global would instead be freely readable and modifiable by any other code in the program, offering none of that protection. This combination of persistence across calls and inaccessibility from the outside is exactly why closures are a common way to implement private state in a language without a dedicated private-field keyword.
3 / 5
In a code review, a dev notices a loop creates several closures inside a `for` loop, each one intended to capture that iteration's own separate value of the loop variable, but the language's loop-variable scoping means every closure instead ends up sharing and reading the same final value. What does this represent?
This is a closure-capture bug, where all the closures created inside the loop unexpectedly end up sharing one single variable instead of each one capturing its own separate value from its own iteration, so when any of them is finally called, they all read whatever the loop variable's value happened to be after the loop finished, rather than the value from when each closure was created. A cache eviction policy is an unrelated concept about discarded cache entries. This is one of the most classic closure pitfalls, and it's exactly why some languages introduced a per-iteration binding specifically to make each closure capture its own separate value by default.
4 / 5
An incident report shows a set of button click handlers created in a loop all logged the same final index instead of each one logging its own button's index, because every closure created inside the loop ended up sharing the same loop variable rather than capturing its own iteration's value. What practice would prevent this?
Creating a fresh, per-iteration binding of the loop variable, whether through a language feature that does this automatically or by explicitly passing that iteration's value into an immediately invoked function, gives each closure its own separate captured value instead of a shared reference to one variable, which is exactly the fix for the click-handler bug described in this incident. Continuing to let every closure share the same loop variable with no per-iteration binding is exactly what caused every handler to log the same final index. This per-iteration-capture technique is the standard, well-known fix for exactly this classic closure pitfall.
5 / 5
During a PR review, a teammate asks why the team needs to think carefully about per-iteration variable bindings when creating closures inside a loop, given that each closure is a separate function. What is the reasoning?
A closure captures a live reference to its outer scope's variable, not a one-time snapshot of that variable's value at the moment the closure was created, so if the loop's scoping rules mean every iteration shares one single variable instead of getting its own fresh binding, every closure created in that loop ends up reading whatever that one shared variable happens to hold whenever the closure is finally called, which is very often not the value the developer intended. Understanding this live-reference behavior, rather than assuming closures automatically snapshot values, is exactly why per-iteration bindings need to be created deliberately in languages or patterns where that isn't the default. The tradeoff is the small extra step of explicitly creating a fresh binding per iteration, which is a minor cost for avoiding a very common and hard-to-spot class of bug.