What are Closures in JavaScript?

Closures in JavaScript refer to a function's lexical scope and the retained reference and state of variables and functions within that scope.

This provides the ability to access an instance of a function and it’s scope (and so access it’s scoped variables and functions that were available at the time of creation) from outside of the original scope.

Or in the words of the Mozilla site, “A closure is the combination of a function and the lexical environment within which that function was declared. This environment consists of any local variables that were in-scope at the time the closure was created.”

If this all just sounds like words, let’s see some code.


  function doSomething(a) {
    function increment(b) {
      a = a + b;
      console.log(a);
    }
 
    return increment; // increment is returned from doSomething
  }
 
  const myNumber = doSomething(1);
  const anotherNumber = doSomething(10);
 
  myNumber(1); // 2 (from console.log(a);)
  anotherNumber(10); // 20 (from console.log(a);)
 
  myNumber(1); // 3
  anotherNumber(10); // 30

The doSomething function takes an argument as parameter a. Within doSomething we create an increment function that takes an argument as parameter b. The increment function adds a to b, assigns it to a and outputs it to the console.

We then return the increment function from doSomething.

The const myNumber is assigned the return value of doSomething, which is passed the argument 1. So within doSomething, a = 1.

As doSomething returns increment, myNumber now references increment. When we invoke myNumber (myNumber(1)) with an argument of 1, we actually invoke increment with an argument of 1 which adds the value of a (1) to b (1) and outputs 2 to the console.

If you invoke myNumber again, the output is 3, because the value of a that’s available to the scope of increment is 2 from the previous execution. This is what is meant by the increment function having access to the original lexical scope, but accessed from outside using myNumber. The increment function has closure over the doSomething function. It’s like an instance of scope where the variables, functions and values are retained for that scope!

This becomes even clearer when you see that the const anotherNumber has access to it’s own value for a, independent of the myNumber assignment. When anotherNumber(10) invokes the increment function, the value of a within scope is 10 from the const anotherNumber = doSomething(10) assignment.

Invoke these references to increment separately again (myNumber(1); anotherNumber(10);) and the outputs are relative to the corresponding values of a for each scope.

Why Do I Need to Know About Closures?

Closures provide a way to emulate private methods in JavaScript and create code modules to expose only what’s necessary to the rest of the codebase, keeping related implementation details private and encapsulated. Search for the Module Pattern in JavaScript for examples.

Understanding closures also helps you understand what’s available in scope when code executes, and certain gotchas.

Take this for example:


  for (var i = 0; i < 2; i++) {
    document.getElementById(`el-${i}`).addEventListener("click", function () {
      console.log(i);
    });
  }

We’ve added an event listener to two elements in the HTML, one with an id of el-0 and one with an id of el-1. On click of those HTML elements, we output the value of i from the for loop to the console.

When you click either of those HTML elements, you see the value 2, rather than the corresponding 0 and 1 for each iteration of the loop. This is because by the time the click occurs i has a value of 2 from the end of the for loop, as the for loop doesn’t create a closure within it’s curly braces.

To amend this to show the respective 0 and 1 for each element, we can create a closure. Creating the closure means, as we’ve described, the reference to lexical scope is retained and still available for access even after the for loop has moved on with updated values for i.

We can create a closure by using an immediately invoked anonymous function (IIFE).


  for (var i = 0; i < 2; i++) {
    (function (j) {
      document
        .getElementById(`el-${j}`)
        .addEventListener("click", function () {
          console.log(j);
        });
    })(i);
  }

If you’re not sure what an IIFE is, have a quick look at this article, but essentially it’s a function that’s called immediately. Passed into that function is the argument i, which is received as parameter i. So within each for loop iteration the function is called, and the IIFE creates a closure, retaining the value of i passed in to the function in the parameter j (it could have been called i also, but j here differentiates between the i that’s passed in, and the j that receives it).

This means we now click the HTML element and the scope lookup for the j in console.log() can look to it’s parent scope where the j reference has been retained by the nature of the closure. We now get 0 when we click the element with id el-0, and 1 when we click the element with id el-1.

This isn’t a hugely practical example, but shows the availability of scope with and without closures.

There’s a magical nature of let in for loops in that they create a closure for the loop.


  for (let i = 0; i < 2; i++) {
    document.getElementById(`el-${i}`).addEventListener("click", function () {
      console.log(i);
    });
  }

So rather than having to create the IIFE closure above, the let creates closure, retaining the value of i for each iteration of the loop, making it accessible later when we click the element. Pretty cool! 😎🍻

If you have any questions or comments or want to connect, you can follow me on Twitter, or sign up to the newsletter in the footer for front-end articles to your inbox 👇


Back home