about
syllabus
All example source code
Closures are a key aspect of the JavaScript programming language.
Closures are functions that refer to independent (free) variables (variables that are used locally, but defined in an enclosing scope). In other words, these functions ‘remember’ the environment in which they were created.—Mozilla Developer Network
This page offers a cursory overview of the idea of a closure along with a couple scenarios particularly relevant to the examples for this course.
Variable scoping is key to the idea of closures. Lets briefly look at how variables are scoped in a JavaScript function.
x
is a global variable so its viewable both outside of and inside logXY()
. y
is local only to logXY()
and is therefore only available inside logXY()
.
Now let’s adjust the scenario above as follows:
If you are used to programming languages like Java or C++, the variable x
, for example, is local only to setup()
. So once setup()
finishes executing its no longer needed and goes away! So when logAgain()
is called later in mousePressed()
, there should be nothing storing a reference to x
and the result should be undefined
.
But it’s not!
This is the magic of JavaScript. Whenever a function is declared, a “closure” which stores both a reference to the function as well as all variables currently in scope for that function is maintained. One way to think about this is that scope isn’t just about where in the code things are declared, but it’s also about when things actually happen. So while x
and y
’s scope are limited to setup()
, they also extend beyond setup()
to anytime that logXY()
function is called.
This idea of keeping scope for “later” is crucial for callbacks and becomes relevant for scenarios like animation and API callbacks. Let’s look at animation first.
Let’s say you have a DOM element.
And let’s say you want to use setInterval()
to change the element’s count every N millseconds.
Now let’s say you wanted to do the same for more than one paragraph, and have the second paragraph keep its own “count”. Sure you could create count1
and count2
and call setInterval()
twice. But better yet, you could use a closure!
Closures are also helpful when querying APIs. Let’s say for example, you have several DOM elements, each associated with a particular term. And whenever you click on any of the elements, you want to make an API call with the associated term.
Now the idea here is that the queryAPI
function would connect to the API itself.
Ooops! There’s no reference to the particular word associated with the element that was clicked on. (While yes, there are ways to get access to that word from the DOM element itself for the purpose of demonstrating the closure I’ll skip that as an option.)
Here what you may be thinking the following: “I want to pass an argument to queryAPI
. In other words, you want to execute the callback queryAPI
with the value stored in words[i]
. But you can’t. That is, unless you use a closure!
Here, the technique is to instead call a function where the callback is defined as a closure that maintains a reference to the particular term associated with the callback. In other words;
And then the assignQuery
pairs the value in words[i]
with the mousePressed
event for the DOM element.
If the gotData()
function is also defined inside assignQuery()
then it can also make use of the closure maintaining a reference to the DOM element and associated word.
Again, the key here is that while the scope of elt
and word
is defined locally to assignQuery
, event though queryAPI
may happen later whenever the user clicks the mouse, long after assignQuery
was executed, that scope is maintained by the queryAPI
’s closure which maintains a reference to all variables within its own scope.
Here is the a full example that queries wordnik and returns substitutes a “related” in the DOM element. And the source code. The example also uses the “animation” closure to count while waiting for data back from the API.
Use a closure to create 100 DOM elements with setTimeout()
. Here is code that doesn’t make use of a closure and does not work properly.
Use a closure to animate a DOM element in some way with the style()
function. (Fill in the blanks).
Then assign the function to a single element:
Then start animating elements only once you click on them:
Now make many elements, each that start animating when you click on them. Do you need a closure now that you are looping through the elements? (Hint: yes, you do except for the fact the p5.js will assign the this
keyword to the element in a mousePressed
callback.)
Finally, can you get your animate()
function to return a reference to the interval so that you can start and stop the animation when you click on it? This one is hard!
Use a closure to make an API call to openweathermap.org. Send openweathermap a zip code and when the weather is returned, create a DOM element with that zip code and the weather data. Here is some code to help you get started.
Now can you make this work with multiple zip codes? Can you make buttons for each zip code that when you click on each button an API call is made for a particular zip code?