A closure is a function that captures the state that it’s surrounded by, closing over said state.
Uh, what do closures do…
Closures let us write code with a higher level of abstraction. A good thing, as abstraction allows us to solve more problems with less code. Now, by example. Let’s write a baby program that counts the occurrence of a particular word in a sentence.
Works, pay me. But wait! What if our program frequently needs to count words in sentences? Sounds like a task for a function:
Alright, okay. But wait! Our function requires two arguments when we call it. What if we don’t know the string that we’d like to search at this point in our program? We can abstract this a bit more with closures. Let’s build a function that returns another function.
Closures “know” about the variables defined in their enclosing scope. This has some interesting consequences when creating functions that return functions. In the last snip,
counter contains a function that we can call like:
We’ve assigned the value returned by
counter() to a new variable. That returned value happens to be another function, so
countTheWordSup essentially becomes the name of a function. Let’s call it:
As if by magic,
countTheWordSup() remembers the value we passed to
counter() as “wordToCount”. Closure by example.
I made a trick on you! I’ve conflated closures with curried functions, as curried functions are what made closures really click for me. Closures are also visible in what is commonly referred to as the module pattern, which is perhaps a more typical introduction to closures.
The module pattern leverages closures to approximate private and public variables in classical languages:
uh returns an object, a property on which is a function that references a variable in
uh‘s scope. The same mechanism is at work, though to a different end; here, we’re doing a bit of data hiding.
There’s quite a bit more to closures, but this introduction should give you enough utility that you’ll continue to explore other usages.