How Closures Work
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:
Now the 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 user
into onlyFavorites
, let it “fall” into the inner function and then return that function. That function will accept the pokemon
that filter
passes to it and still remember the user
.
The onlyFavorites
function is the mug.
The onlyUserFavorites
function is the shot glass.
The user
is the almond we dropped into the mug that fell into the shot glass.
The pokemon
that filter passes in is the cashew.