Closures

  • Code
  • JavaScript
  • Scope
  • Closure
  • Fundamentals

Now that we have a firm grasp on how Scope works in JavaScript, it's time to turn our attention to a related topic, and one that trips up a lot of developers (myself included): closure.

Closure happens when a function is able to access its lexical scope even when it is executed outside of its lexical scope. This is typically done through a combination of an outer/external function which contains some variables and any number of inner functions that get returned from the outer function. When the outer function gets called, the inner function(s) can be invoked to access the variables declared in the outer function, even though those variables themselves aren't returned. The inner function is said to "enclose" the outer function's scope (including the internal variables). This is called a closure.

Let's take a look at a function and variable within an external variable's scope:

function aloha() {  var greeting = "Howdy!";
  function greet() {    console.log(greeting);  }
  greet();}
aloha();// expected result: "Howdy!"

We declare an outer function, aloha(), and within that a variable, greeting, with the value "Howdy!" as well as a function, greet() that console.log()s the greeting variable. Finally we call the greet() function. Then back outside of the external function we call aloha(). This function call actually invokes the greet() function, which accesses the greeting variable, and we get "Howdy!" logged out to the console. The greeting variable and greet() function are scoped to the aloha() function. Nothing majorly complicated is happening here!

But now let's take it one step further and actually demonstrate closure:

function aloha() {  var greeting = "Howdy!";
  function greet() {    console.log(greeting);  }
  return greet;}
var salutation = aloha();
salutation();// expected result: "Howdy!"

We made two crucial changes for the above to work:

  1. Rather than just calling the internal greet() function, the external aloha() function actually returns the internal greet() function
  2. Rather than just calling the aloha() function globally, we assign the value returned by invoking aloha() (i.e., the internal greet() function) to the variable salutation

In this way we're calling greet() outside of its authored lexical scope, but it still maintains its scope! And even though we aren't returning greeting from the outer aloha() function, we're able to access greeting's value through the greet() function. All of this illustrates a closure. Specifically, the greet() function has closure over the contents of the aloha() function.

Function Factory

Let's take a look at a more sophisticated example of this pattern:

function add(num1) {  return function (num2) {    return num1 + num2;  };}
var add400 = add(400);var add600 = add(600);
console.log(add400(20));// expected result: 420
console.log(add600(66));// expected result: 666

We have our outer function, add() which has a parameter, num1. Within add() we are returning an anonymous function, which has its own parameter, num2. This anonymous function adds num1 and num2 and returns the value of that sum.

Below that we declare a variable, add400, and pass it the add function with the argument 400. 400 will be our num1.

Below that we do the same, but the variable declared is named add600 and 600 is passed as the argument. We're reusing the add() function, but this time around 600 will be num1.

Finally we actually call add400() and add600(), passing them the arguments 20 and 66 respectively. 20 and 66 will be num2 within our addition equations. 20 gets added to 400 because add400() has closure over the add() function with 400 as the value of num1, and the resulting value is 420. Similarly, 66 gets added to 600 because add600() has closure over the add() function with 600 as the value of num1. The resulting value here is 666.

We have essentially created a function factory with the add() function using closures! It's a fancy way of being able to reuse functionality across your program.

Modules

Modules have been covered before on this blog, but to recap, the module pattern is a way of bundling related pieces of data and functionality within a program that can be reused or called when needed. To start with, we need a function. Let's look again at our aloha() function from before:

function aloha() {  var greeting = "Howdy!";
  function greet() {    console.log(greeting);  }
  greet();}
aloha();// expected result: "Howdy!"

At this moment there's nothing really special going on, we're calling the outer function and it's calling its inner function to log "Howdy!" to the console. No closure, no module, just accessing some scope. But let's change it in a different way this time:

function Aloha() {  var greeting = "Howdy!";
  function greet() {    console.log(greeting);  }
  return {    greet,  };}
var salutation = Aloha();salutation.greet();

This differs from our first closure example above in a few important ways:

  1. aloha() has now been capitalized to Aloha(). This is common practice for working with modules and other constructors. It signals to developers that you're deliberately embracing one of these code patterns
  2. Where before we simply returned the greet() function within aloha(), here we have created an object to return and passed greet as a property on that object

Now when we declare the salutation variable, we pass it the Aloha() function call as a value, but in order to actually run the internal greet() function, we have to call it as salutation.greet() instead of simply salutation(). greet is made available as a method on salutation.

This little example, while trivial, exemplifies the two requirements for the module pattern:

  1. There has to be an outer enclosing function that gets invoked at least once; each time it's invoked it creates a new instance of the module
  2. The enclosing function must return at least one inner function that has closure over the outer function's scope; it can access and modify values and functionality within that scope, but unless those values and functionality are deliberately returned from the outer function, they are treated as private

An example of the second point is the variable greeting; the only way to actually access and use that variable is through the greet() method on an instance of the module. As such, this is a way to keep certain data private but still available for use in your program.

Where we could invoke our Aloha() module any number of times and create a new instance of the module each time it's invoked, we are also able to wrap the outer enclosing function within an IIFE and use that to invoke the module; this pattern only allows for a single instance of the module to be created:

var salutation = (function Aloha() {  var greeting = "Howdy!";
  function greet() {    console.log(greeting);  }
  return {    greet,  };})();
salutation.greet();// expected result: "Howdy!"

You can think of the module's return statement as its public API that allows you to modify the functionality or values within that module instance. This is super powerful stuff:

function Aloha() {  var statement = "Howdy!";
  function change() {    statement = statement === "Howdy!" ? "Adios!" : "Howdy!";  }
  function greetOrDismiss() {    console.log(statement);  }
  var publicAPI = {    change,    greetOrDismiss,  };
  return publicAPI;}
var greetingOrFarewell = Aloha();greetingOrFarewell.greetOrDismiss();// expected result: "Howdy!"
greetingOrFarewell.change();
greetingOrFarewell.greetOrDismiss();// expected result: "Adios!"

We've added at change method on the module instance that changes the greeting between "Howdy!" and"Adios!"` when it gets called.

Closures and Loops

Before block scoped variables declared with keywords like let and const, closures were a way to help avoid scope pollution issues with loops in JavaScript.

Consider the following code, taken from my February post, Frontend JavaScript Pop Quiz:

for (var i = 0; i < 5; i++) {  var btn = document.createElement("button");  btn.appendChild(document.createTextNode("Button " + i));  btn.addEventListener("click", function () {    console.log(i);  });  document.body.appendChild(btn);}

If you attach this code to an HTML doc, it appends 5 buttons (with the text "Button 0", "Button 1", "Button 2", "Button 3", and "Button 4"), and attaches an event listener (specifically a click event listener) to each button to log i to the console. However, with the code written as-is, rather than getting the number of the button that we clicked, we get 5, regardless of if we click "Button 0" or "Button 3" or any other button. That's because the var i variable in the for Loop head has polluted the scope, and on the last iteration of the loop, i's value gets incremented to 5.

There are lots of solutions to this issue, but one approach that embraces closure (and is therefore relevant to this blog post) is to use a callback function within an IIFE:

for (var i = 0; i < 5; i++) {  var btn = document.createElement("button");  btn.appendChild(document.createTextNode("Button " + i));  (function wrapperIIFE(i) {    btn.addEventListener("click", function logI() {      console.log(i);    });  })(i);  document.body.appendChild(btn);}

We create an IIFE named wrapperIIFE that has a parameter, i. Within wrapperIIFE() we have the click event listener attached to btn (accessible because the IIFE is within the scope of the for Loop), and that event listener's callback function, logI(), has closure over the wrapperIIFE, and thereby can access wrapperIIFE's i value in order to log it to the console. Lastly when we actually invoke the IIFE, we pass i as its argument.

Now if we run this code we get the correct button numbers logged to the console when any of the buttons are clicked. This is still not my favorite solution – if you log i in the console (outside of any loops or clicks), you'll still get 5 because the var variable has still polluted the scope. The closure/IIFE solution here doesn't address the scope pollution issue.

Summary

A Closure happens when a function is able to access its lexical scope even when it is executed outside of its lexical scope. This is typically done through a combination of an outer/external function which contains some variables and any number of inner functions that get returned from the outer function.

One use for the closure pattern is to be able to make so-called "function factories" - functions that produce other functions; actual functionality gets reused, but different parameters can be used to generate different results. An addition function is a good example of this.

The module pattern is a specific use of closure, and is a powerful way of bundling related pieces of data and functionality within a program that can be reused or called when needed, while keeping data and functionality that you want hidden to stay secret within the module instance. There are two requirements to meet this pattern:

  1. There has to be an outer enclosing function that gets invoked at least once; each time it's invoked it creates a new instance of the module
  2. The enclosing function must return at least one inner function that has closure over the outer function's scope; it can access and modify values and functionality within that scope, but unless those values and functionality are deliberately returned from the outer function, they are treated as private

Closures also provide a way to address common problems surrounding scope and iterators in loops.

Sources / Further Reading