Originally published 1.26.18 as part of JavaScript January. The original post can be found here
Welcome to what will (hopefully) become a series of blog posts around some of the fundamental concepts in JavaScript. The inspiration for this series came from my time as a student at the Turing School of Software & Design. When I first started programming, I found code examples using variable and function names like Foo, Bar, Baz, Bash
to be extremely confusing.
For example, from one of Kyle Simpson’s talks about scope:
var foo = “bar”;
function bar() {var foo = “baz”;}
function baz(foo) {foo = “bam”;bam = “yay”;}
For a lot of folks who might have been programming for a while, this is straight-forward and easy to understand. To a beginner, this can be extremely confusing. It’s a pattern so often used that, when I was first learning JavaScript, I thought foo, bar, bam, baz, bash
were somehow special keywords like return
and function
.
When looking at code examples, the reader should be able to determine which parts of the example are essential, and which are non-essential like variable names, function names, arguments etc. In this post we’ll use sandwiches, and the folks who make them, as examples for talking about JavaScript’s closure
. The ridiculously long variable and function names should make it easy to distinguish what parts of an example are part of the JavaScript language and syntax; and what parts are up to you as the developer to name.
We’ll assume that you’ve already been writing a bit of JavaScript, that you understand functions and function declarations. Being familiar with scope and Lexical Scope is necessary in order to understand the power of closures, so if you’re not familiar with these concepts, take a little time to read up on them and come back.
This is a question that often gets asked in technical interviews. Closures are not unique to JavaScript, but they are one of the things that makes JavaScript so powerful. Closure is a computer science term that defines how a function can maintain a record of the environment in which it was called. This means that a function can keep track of the arguments and variables it was initially called with, even when it’s called outside of that scope. Put a slightly different way, the MDN Docs define closure as “the combination of a function and the lexical environment within which that function was declared.” If that doesn’t quite make sense, don’t worry, we’ll have it all figured out by the end of this post!
All JavaScript functions are closures. Functions have access to the variables defined within the function, along with variables defined in any enclosing scope(s), like the global scope. Other common places where closures occur are setTimeout
, event/click handlers, and objects.
As we stated above, you will use closure when defining click handlers, writing callbacks, or when you’re using setTimeout
or setInterval
.
A setTimeout
function might look something like this:
Where we have declared our greetTheCustomer
function, we have created a new scope. Without closures, the function would not have access to customerName
or deliEmployeeName
variables and we would have to explicitly pass those to greetTheCustomer
. In this example, we’ve declared our variables on the global scope, but the concept is the same if this were all defined within a function, or as a method on an object.
Closures are very useful when defining private variables inside an object:
In this example, we’ve made the bread
and the topping
publicly available by defining them as properties on the Sandwich
object. The typicalNumberOfPickles
variable is not available outside of the Sandwich object, in order to access it outside the scope of the Sandwich
object, you can write a method using that variable, and that method will act as a closure.
Here, we’ve exposed a public method howManyPicklesAreOnMySandwich
to give the customer a crucial bit of information. Even though we do not have direct access to the typicalNumberOfPickles
variable in the global scope, thanks to the closure, we can get that information through our new method.
A pattern often used in place of an object with a single method, is to take advantage of closure by creating a function that returns an inner function. That inner function will have access to any variables defined in the outer function, without ever exposing those variables to a larger scope.
Here our morePicklesPlease
function maintains access to the original typicalNumberOfPickles
variable and will continue to update it. It’s important to note that each time you store the returned function for later use, it forms its own closure. The original variable is not shared between each function, each function has its own copy of that original environment.
In the example below, we’ll make 2 different sandwiches from the same askForMorePickles
function, each sandwich will have its own number of pickles.
This allows the consumer to tailor their sandwich experience to the perfect number of pickles!
Because each version of a closure keeps a copy of that original environment, that environment isn’t removed from the callstack until all of the closures using it have been garbage collected. So, for example, if you had 100 different functions referencing that original variable, and 99 of them get garbage collected, that variable still exists until the 100th function gets collected. This uses up local memory and eventually can lead to memory leaks.
Closures are a fundamental part of many programming languages, not just JavaScript. A closure refers to a function maintaining access to the free variables defined where the function was called. Some use cases for closures are event handlers, timeouts, intervals, callbacks and keeping variables private within functions. One drawback to using closures is that they can lead to over-consumption of memory, and possibly memory leaks if not handled properly.
Hopefully this has helped de-mystify closures for you a bit. I would highly recommend the You Don’t Know Javascript: Scope & Closure book if you’d like to learn more.
If you enjoyed this article, and would like to see future posts, please let me know here or on Twitter: https://twitter.com/Jeff_Duke_io