Closures can seem like a daunting topic to anyone who’s relatively new to programming. They represent a topic that lies beyond the core programming concepts that are part of most introductions to the field — primitive data types, variables, functions, basic control flow, etc.
Nevertheless, closures are a very straightforward concept, so I want to explain them as I understand them, and provide some examples to illustrate why you might want to use them. In this way, they can go from being an abstract concept to a technique you can actually deploy in your everyday code.
What is a closure?
The term ‘closure’ refers to a function that is bundled with everything that was in scope when it was created.
Let’s examine why that’s not intuitive.
In the code below, everything works exactly as we expect:
Now consider the following:
We have two functions,
nested. When the first function is called, it returns the second one. In the code above, by the time we call
nestedFunc (which is a reference to the
nested function), the
outer function has already returned. We don’t expect that
nestedFunc can access the variable
two when it is called, since that variable is not in scope when we call it.
And yet, it does. This is a closure — a function that has access to all of the variables that were in scope when it was declared.
1. Tracking DOM State / Styles
There are quite a few use cases for closures when it comes to working with the DOM. One thing you might want to do, for example, is keep track of the initial styles of some DOM element, even over the course of multiple style changes.
Suppose you had the following markup:
and you wanted to be able to programmatically change the div’s styles, while keeping track of its initial styles in the event that you wanted to revert back to them. Closures can help us out:
The above code is a function that returns two things when passed a valid DOM selector — a getter for the initial value of any style prop on the element, and a setter to modify any value.
Note that we need to assign
window.getComputedStyle to a new object, otherwise every time we try to access properties on
initialState, the values will get recomputed and the initial styles are lost.
We could consume it as follows:
We can create as many of these as necessary for whatever DOM elements we’re interested in tracking or updating.
The term ‘singleton’ refers to a design pattern in which a given class is only instantiated once and only that one, single instance is ever made available publicly. This is a somewhat controversial design pattern for reasons that probably make more sense in a strict OOP architecture, but I think singletons can still be useful for some isolated services that are not a core part of your business logic. Folks with more experience in OOP will probably have a lot more to say on this topic.
In the example below, we use the singleton pattern to construct a really naive logging service:
The above code makes use of the IIFE or Immediately Invoked Function Expression to return the object with the
error methods on it. That’s why we didn’t have to call the service like a function, i.e.
LoggingService() — it’s not a function, but rather the return value of the function.
As we can see if we run this, the three methods on the
LoggingService object have access to the string variables in the closure. We could augment this service by adding whatever variables we want, they will all be available to the log methods.
This might not be the best approach for large applications where we might have multiple processes running, each with their own logger, as there is no way to configure the options for this logger from outside. In that scenario, we’re probably better off creating a logger class that we can instantiate with different configuration options.
3. Higher-Order Functions
A higher-order function is a function that either takes one or more functions as arguments or returns a function. This is a really useful concept that we can use to help us avoid having to write boilerplate code over and over. We can create higher-order functions that, in turn, create utility functions we can use any time we find ourselves repeatedly writing similar code.
One simple example is a function that creates functions to help us round integers to a certain number of decimal places. Imagine in some cases we have a float that we want to round to the nearest whole number, while in some cases we want to maintain two decimal places.
Here is the (somewhat) long version:
We can use closures to create a utility function that makes this a little bit easier to work with:
The way we use closures in this example is by ‘closing over’ the argument to
rounder has returned, we wouldn’t expect the inner function to have access to the
places parameter, yet that variable is preserved in the closure.
Following this example, we can create an arbitrary amount of ‘rounders’ for all our rounding needs…or generate-a-random-number-in-a-range needs, or string formatting needs, and so on. You get the idea.
A note on private methods
classInstance.privateMethod() throws an error. These methods are called internally by other methods of the class, but are themselves not directly accessible from outside.
The new syntax for declaring private methods is to prepend a
# to the variable name, as follows:
That being said, here is how we would’ve accomplished this with closures:
I hope the notes above alleviate some of the confusion around the topic of closures, and inspire you to investigate more functional programming techniques!
Wikipedia article on closures: https://medium.com/r/?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FClosure_%28computer_programming%29
TC39 Class fields proposal: https://medium.com/r/?url=https%3A%2F%2Fgithub.com%2Ftc39%2Fproposal-class-fields
Misko Hevery’s article on Singletons: https://medium.com/r/?url=http%3A%2F%2Fmisko.hevery.com%2F2008%2F08%2F17%2Fsingletons-are-pathological-liars%2F