Closures in JavaScript

Use cases from an object oriented perspective

Ellis Andrews
codeburst

--

Every year it seems JavaScript is brought closer and closer to being a full-featured object oriented language. Initially thrown together as an accessible client-side scripting language (stop me if you’ve heard this one…), JavaScript has come a long way in the past 25 years. Tools like Node.js have even made JavaScript a viable — nay, useful — server-side language, and thus we continue to see core OOP components adopted into the JS lexicon. One such example was the introduction of the class keyword to the language in ES6, which provides a familiar interface for defining objects a la Java, Python or Ruby. Even if it is mostly syntactic sugar, it speaks to the direction the language is heading in the modern age. In keeping with the times, I’ll use the lens of OOP to demonstrate a couple of common use cases for closures in JavaScript.

So… What is a Closure?

If you’re unfamiliar with what a “closure” is, it can roughly be defined as:

Closure A function that is a first-class citizen and has access to variables local to the scope in which it was defined.

In JS, functions are first-class citizens. This just means that you can do things like assign them to variables, pass them as arguments, return them from other functions, etc. (like you would, say, an integer or a string). So that part is taken care of for us out of the box.

But what about that second part about accessing variables? To understand this, we first need to understand how JavaScript handles scope. A whole article of its own could be written on this topic, but the key things to know are that:

  1. Functions each have their own local scope.
  2. Functions have access to variables that are local to the scope in which they are defined.

This is easiest to see through example:

The cool thing to notice here is that even though we call the innerFunction (assigned to the variable closure) after the outerFunction has finished executing, it still has access to outerFunctionVar and remembers the value! We’ve created our first closure 🎉.

So… What can we do with Closures?

As always with my technical writing, we’ll dig in with a tangible example.

Meet my dog, Biscuit.

Biscuit
Yes, this is actually my dog Biscuit.

The picture is great and all, but let’s represent her in code. And for good measure, I’ll write my relationship to her as well. Let’s be OO about it.

For the sake of example, say the year is 2012 and I’m headed off to college. Unfortunately, I won’t be able to bring Biscuit with me 😢. I’ll have to transfer ownership of her to my trusted friend Michael Bluth, which I’m fully able to do.

That worked. The problem, though, is Michael can also reassign Biscuit’s ownership. In fact, anyone could reassign her name and breed as well! I certainly don’t want Michael renaming or giving away my dog while I’m away, and frankly I’m not even sure what reassigning her breed would mean in this context. We’ve hit our first use case for closures in object oriented JavaScript.

1. Object Data Privacy

The idea of private instance variables and methods is one major OOP concept that has yet to be well integrated into JavaScript*. Any property defined in an ES6 class is automatically public, and can be accessed and modified on an instance at will. One way to get around this is to use a closure.

In our example, I do not want the name or breed of my dog Biscuit to be able to be changed — only read. I do want her owner to be able to be changed if necessary, but I want to set some boundaries about who is allowed to be assigned as her owner. Here’s how I could redefine my original Dog object to use a closure in order to manage access to Biscuit’s properties:

We’ve done away with the class Dog syntax. Now, we store the instance properties as variables within the local scope of the Dog function. We then use the fact that functions defined within a function have access to the local scope of the outer function to write “getter” and “setter” functions that interact with those variables for us.

If it helps, you can think of this like an API: We cannot touch the name/breed/owner data directly, we have to access it through a middleman (the defined functions). The middleman may have rules about what we, as the user, are allowed to see or edit.

Note that there are no setter methods for _name or _breed, only getter methods. That achieves our goal of being able read those properties, but not change them! While there is a setter method for _owner, in that method we’ve enforced that this property can only be set to a Person who has an eligible last name.

I think Michael is a stand up guy, but now I know for a fact that while I’m at college he cannot:

  1. Rename Biscuit.
  2. Change Biscuit’s breed.
  3. Give Biscuit away to an unapproved owner (or no owner at all).

I can rest easy in my twin XL.

*ES2019 introduced # names for private instance variables, but not all developers are satisfied with this solution and no such concept yet exists for private methods.

2. Partially Applied Functions (and Factories)

Now, let’s extend this example to our second use case for closures in object oriented JavaScript. Imagine I have a huge family — actually, I do — and I want an easier way of creating instances of my simple Person class so I can represent everybody. And let’s say I also want to be able to do this for Michael Bluth’s family, whom I’ve never met but I hear are great people. It would be nice if I didn’t have to write out the same two family last names over and over again, because they’re not changing!

Thanks to closures, there’s a way:

In the closure above, the inner newFamilyMember function just returns a new instance of the Person class. However, by wrapping it in an outer function, we’re effectively* able to create a partially applied version of it where the family name is locked in! When we call the outer familyMemberFactory function with a last name argument, we get back a function that will create a member of that family by accepting only their first name as an argument. We can call that factory any time we want to add a family member!

*For this to truly be a partially applied function, you’d have to imagine the newFamilyMember function taking a second argument of lastName initially, but I’ve eschewed that here to emphasize how we’re leveraging lexical scope in the closure.

There are other use cases for closures, but these are the ones I had time for today. Let me know in the comments if you have found any particularly interesting alternatives! Oh, and not to mix sitcom references, but by now I hope you’re feeling like Rachel in this gif when it comes to closures. I know I am.

--

--

Full stack developer working to demystify code through example. Mostly Python and React.