Let me tell you a story about a mug, a shot glass, an almond, and a cashew. Lets say you have a mug with a shot glass in it:
Then, you drop an almond in the mug, and it falls into the shot glass:
When you pour out the mug, you get the shot glass with the almond in it:
You can even add more nuts if you want:
When you pour out the shot glass, you have both nuts, even though the almond was originally dropped in the mug, and the cashew was dropped in the shot glass:
The shot glass got the cashew as an argument and the almond through closure.
A closure is when an inner scope uses something from an outer scope. The inner scope “remembers” its outer scope when it’s defined, and can use any variables from that scope.
For example, let’s say we have a user object and a list of Pokemon. We want to filter the list of Pokemon so we only return the users’ favorites:
We can use
user in the inner scope even though it was defined in the outer scope because inner scopes access outer scopes via closure.
Now let’s say we want to refactor that code to extract out that anonymous function being passed to filter:
onlyFavorites function is dependent on its environment; if we move it to another module, the code will break if
user isn’t also defined there. It’s also harder to test because the function has an indirect input. Not good.
Closures to the rescue again:
We can pass
onlyFavorites, let it “fall” into the inner function and then return that function. That function will accept the
filter passes to it and still remember the
onlyFavorites function is the mug.
onlyUserFavorites function is the shot glass.
user is the almond we dropped into the mug that fell into the shot glass.
pokemon that filter passes in is the cashew.