JavaScript Closures Explained by Mailing a Package

If you have mailed a package or letter in the past, then you can understand closures in JavaScript.

On your journey to becoming an intermediate or advanced JavaScript dev, you may have come across closures. After reading a technical resource on the subject… you also probably ran in the opposite direction.

Here is the awesome thing about closures: they allow you to save a snapshot of the scope when the function was originally declared.

Since they exist within outer functions, they allow you to execute the outer function at one point in time, and then execute the closure later, with the values from the outer function saved.

It’s like adding a ‘pause’ button to your outer function.

I was trying to think of a good example of the functionality of closures in the real-world , and I thought about sending gifts during the holiday season.

In order to understand closures, you just need to know about the concept of scope in JavaScript. Check out my other tutorial if you need a refresher.

If you are looking for a technical explanation of closures, the guide on MDN is probably the best.

What is a closure?

Imagine that you are packing from the holidays, but your memory isn’t working very well.

You are hoping to send gifts and cards to all your friends and family. But, as soon as you pack a box, you must immediately put an address on it. If you forget to put addresses on your boxes, you will need to cut them open and try to remember who to send them to.

Of course, you could start keeping a separate list of which package is for who… but that is incredibly inefficient! Instead, it would be best if you could pack all the boxes at once, then add addresses to them whenever you want. You know, the normal way a brain is supposed to work.

In code, this is similar to using one function to do all the packing, and another function to address the packages. And, you would want the addressing function to be inside the packing function, since you always do it afterwards.

So, you can use a closure to put one function inside the other, and still execute the inner function at a later point with a saved reference to what is actually inside the darn box. No back-end or database needed.

This is better than calling two separate functions because you will want to write clean and maintainable code for later review. And, these two functions are linked, so you want to represent that in code.

A closure looks like this:

  1. An outer function
  2. An inner function
  3. A return statement that references the inner function

Like this:

function outer(){

  function inner(){
  }

  return inner
}

When the heck would you use a closure?

Let’s say you are building an interactive map of tourist landmarks in New York City using the Google Maps API. You have an array with a bunch of map markers that you want to add to the map- the Statue of Liberty, Empire State Building, Coney Island, you name it. You want to add all these markers to the map, but you also want to add a click event to each marker. When you click the marker, you want to show dynamic information about that marker, including live weather data.

var touristPlaces= […];

for(let i=0; i < touristPlaces.length; i++){
  let marker= touristPlaces[i];
  $(marker).click(function(){
    showToolTip(i)
  });
}

Here’s the issue- if you write it like this, it will not work. The ‘for’ loop will finish before the callback in the click event can register the appropriate i value. You need to capture this intermediate point so you can call the function later with the appropriate i.

An example of a closure

Let’s look at some basic code that uses a closure to mail a package. Here is the first part, where we are declaring the packBox function.


function packBox(item){
console.log('Put ' +item+ ' in the box');
function addressPackage(address){
console.log('Addressed the box to ' +address+' and ready to send the '+item+' gift');
}
return addressPackage;
}

view raw

closurePart1.js

hosted with ❤ by GitHub

In this case, packBox is the outer function, and it uses item as a parameter. The inner function is addressPackage, which takes address as a parameter.

The addressPackage() function is a closure! It can be called at any time after the packBox function has been called. It also has access to the variables and arguments from the time when packBox() was originally called.

That means that item is accessible to both the outer and inner function, while address is only accessible to the inner function.

Here is how you call the packBox function and the closure inside of it.


function packBox(item){
console.log('Put ' +item+ ' in the box');
function addressPackage(address){
console.log('Addressed the box to ' +address+' and ready to send the '+item+' gift');
}
return addressPackage;
}
let brotherGift= packBox('jersey')
brotherGift('123 Main Street, Anywhere USA 012345')
//Put jersey in the box
// Addressed the box to 123 Main Street, Anywhere USA 012345 and ready to send the jersey gift

Notice how the console.log output does not show until lines 14 and 15? This is extremely important. If you ran this code after line 11, you would simply see ‘Put jersey in box’ and the addressPackage function in the console. There would be no error, but the closure, addressPackage(), would not run at that point.

Here’s how to trace those steps

Line 11: You create the variable brotherGift, which is an instance of the packBox() function. You are sending a jersey to your brother.

Line 3: Your code logs a statement about the jersey.

Let’s stop here, and assume that line 13 has not run yet.

Here is what is happening: The packBox() function will not return the “ready to send” line until you also call the addressPackage() function with an argument. Just like there are two steps to sending a package: first, filling it, and second, addressing it. Your package is worthless if it has no contents or it does not have an address!

That being said, you do not necessarily need to address the package directly after you fill the contents. You can wait a few days before addressing it. You might need to go to your computer to look up the address. You might be waiting for your brother to officially change his address!

This interactive graphic will explain.

Regardless, if you do not address the package immediately, this does not mean that the package will somehow magically empty itself. The contents will still be there when you return to address it! They will be stored in the brotherGift variable. So, any time we call brotherGift, the first argument, jersey, will still be available.

…Waiting…Waiting…Now let’s run line 13.

Line 13: Alright, let’s finish off this instance! You are ready to add the address, so you call brotherGift and offer the address as an argument.

Remember from line 11, brotherGift is an instance of packBox with the ‘jersey’ argument. So when you call it, you are offering another argument, which will then be sent to the closure: addressPackage();

Line 3: The console.log will show since we are now running the code from line 13.

Line 4: We now offer the second argument to addressPackage();

Line 8: The return statement can fire for this instance.

Line 6: addressPackage logs a statement related to the address argument.

If we wanted to do this in one line, we would write: packBox(‘jersey’)(‘123 Main Street, Anywhere USA 01234’);

One More Example of A Closure

Let’s say that you wanted to send a gift to each member of your family. You might pack each box before adding the addresses to each. This is what that looks like in code.


var brotherGift = packBox('jersey')
var motherGift = packBox('iTunesCard')
var fatherGift = packBox('golfclubs')
var sisterGift = packBox('lacrossestick')
brotherGift('123 Main Street, Anywhere USA 01234')
//Put jersey in the box
// Addressed the box to 123 Main Street, Anywhere USA 01234 and ready to send the jersey gift
motherGift('123 High Street, Los Angeles USA 01234')
//Put iTunesCard in the box
// Addressed the box to 123 High Street, Los Angeles USA 01234 and ready to send the iTunesCard gift
fatherGift('123 Upper East Street, New York City NY 01234')
//Put golfclubs in the box
// Addressed the box to 123 Upper East Street, New York City NY 01234 and ready to send the golfclubs gift
sisterGift('123 Top Street, Chicago IL 01234')
//Put lacrossestick in the box
// Addressed the box to 123 Top Street, Chicago IL 01234 and ready to send the lacrossestick gift

Why does this work? Because brotherGift, motherGift , fatherGift and sisterGift are all storing separate instances of the addressPackage function. They are created each time packBox() is run. And, they are also storing the value of item when packBox was run. That is where scoping comes into play- these addressPackage instances can still reference the value of item at the time that packBox was executed.

Use this interactive graphic to check it out.

Each instance is able to use the correct gift item with the correct address, even though we run the function with 4 separate gift/address pairs.

Get More Visual Tutorials

Looking for other JavaScript concepts explained? Check out the CodeAnalogies blog or sign up to get the latest ones below.

3 thoughts on “JavaScript Closures Explained by Mailing a Package

Leave a Reply to kkononenkoCancel reply