Paying attention to Closures is considered a requirement for writing clean and "healthy" code over time. In this article, we will discuss at length what closures are, how they work, why we need them and how to use them.

The cool thing about closures is that you are already using them. When writing JavaScript functions, you are already leveraging the power of closures!

What is a closure and how do they work?

Closures, also known as lexical or function closures, are combinations of functions bundled together with references to their surrounding state.

Table of Contents

    In other words, a closure give you access to an outer function’s scope from an inner function outside of its original usage.

    A Simple Example

    As a simple example, let's create the simplest closure we can possibly create. We'll create a function nested inside another function:

    function jump() {
      var height = 10;
    
      function scream() {
        console.log(height);
      }
    
      scream();
    }
    
    jump(); // logs 10

    Here we have a function scream() that has access to the scope of the function jump(). This means that scream has access to the height variable.

    Have we created a closure? Not just yet. This is part of the way there. Let's change our jump function to return the scream function and expand on this by saving the jump function to a new variable called newJump.

    function jump() {
      var height = 10;
    
      function scream() {
        console.log(height);
      }
    
      return scream;
    }
    
    var newJump = jump(); // function runs but doesnt log anything
    
    // you would think that now our code has run
    // you would think that javascript has gotten rid of our height variable
    
    // but we can still use the newJump function as if our content was still intact
    newJump(); // logs 10

    Notice how we save the output of a function to a new variable. The jump function ran and processed itself and we saved the contents to a new variable. Should the height variable exist still after the function already ran?

    What JavaScript does is keep a reference to that original scope and we are still able to use it and the height variable. This reference is called closure.

    Another Example

    Let’s say we want to use a closure; we define a function (our inner function) b inside another function (our outer function) a and expose b.

    To expose b, we simply return it or pass it into a. b will now have access to the variables within the scope of a even after a has been returned.

    function add (a) {
      return function (b) {
        return a + b
      };
    }
    
    // use
    var addUp7  = add(7)
    var addUp14 = add(14)
    
    console.log (addUp7(8)); // 15
    console.log (addUp14(12)); // 26

    In the example above, we have defined a function add(a), which takes a single argument, a, and returns a new function. The function it returns takes a single argument, b, and returns the sum of a and b.

    addUp7 and addUp14 are closures. It creates functions that can add a specific value to their argument. In the example above, add is used to create two new functions — addUp7 and addUp14 which share the same function body definition but contain different lexical environments. In the lexical environment of addUp7, a is 7 while in addUp14, a is 14.

    Why do we need closures?

    Developers are always searching for more advanced coding skills to simplify our daily job and make us more productive. Understanding and applying closures in your code are one of these skills. Here are a couple of ways you could benefit from using closures:

    Data Encapsulation

    Closures give us the ability to store data in a separate scope and share it only when necessary. Creating private static variables that contain data are also possible which is awesome because you can’t get at the data from an outside scope except through the object’s privileged methods. You can define a class inside a function and define the private static variables inside the closure:

    (function () {
        var foo;
        foo = 0;
        function MyClass() {
            foo += 1;
        }
        MyClass.prototype = {
            howMany: function () {
                return foo;
            }
        };
        window.MyClass = MyClass;
    }());

    The foo variable is accessed in the MyClass constructor and the howMany method. Even when the IIFE method is executed and exits, foo's value is still retained in the constructor and the method. This is a very popular use case that you will meet without even knowing it’s closure in place.

    Higher Order Functions

    Closures are really useful when we are dealing with higher-order functions especially when we want to communicate state. Code that is so verbose can be reduced to a one-liner just like the example below:

     // Return a list of all albums with at least 'x' 
        copies sold.
    
     function bestSellingAlbum(x) { 
       return albumList.filter(
         function (album) { return album.sales >= x; }
       );
     }

    In our illustration above, x is communicated step by step from where it is defined to where it is used. The scope of x is also limited, and filter does not have to be written to allow the possibility of passing client-defined data. The need for defining intermediate structures for the sole purpose of communicating the value of x is eliminated.

    Without closures, we would have to impose restrictions on both the bestSellingAlbums code and the filter code, such as a specific interface or user data argument, to be able to communicate the value of x. That would tie both functions together in less reusable ways.

    Thanks to ES6, the code becomes much simpler and can actually be on a single line:

    const bestSellingAlbums = (x) => albumList.filter(album => album.sales >= x);

    In your code, look for places where you generate a lot of boilerplate just to communicate temporary values from one location to another. These are excellent opportunities to consider replacing with closure.

    Best Practices with Closures

    Closures allow us to separate our concerns very cleanly. A handful of ways we could employ closures include the following situations.

    Working with object-oriented programming:

    Closures make working with objects and data easier. A brief example:

    function count() {
      var x = 0;
      return {
        increment: function() { ++x; },
        decrement: function() { --x; },
        get: function() { return x; },
        reset: function() { x = 0; }
      }
    }

    In the example above, x is defined and then accessed in the methods increment, decrement, get and reset. count() is a function that event when executed reserves the x value so the functions in enclosed can access it.

    Passing values and parameters into an algorithm:

    When creating algorithms, we tend to declare functions as well as variables and in a bid to communicate properly, mention them in our code over and over again. Closures help us avoid the need for all that:

    function proximity_sort(arr, midpoint) {
        return arr.sort(function(x, y) { x -= midpoint; y -= midpoint; return x*x - y*y; });
    }

    In the algorithm above, the function proximity_sort takes two arguments arr and midpoint. A second (inner) function with two arguments x and y is nested in proximity_sort, and from it, the arguments x and y as well as the argument midpoint, are accessed. Such use of closures helps us shorten algorithms that would have required a few more lines of code and make problem-solving easier.

    Namespacing private functions:

    Although JavaScript does not have the built-in ability to declare methods as either public or private, it can emulate this functionality via closures:

     var houseRent = (function() {
       var rent = 100000;
       function changeBy(amount) {
         rent += amount;
       }
       return {
         raise: function() {
           changeBy(10000);
         },
         lower: function() {
           changeBy(-10000);
         },
         currentAmount: function() {
           return rent;
         }
       };
     })(); 
    
     alert(houseRent.currentAmount()); // $100,000
     houseRent.raise(); 
     houseRent.lower(); 
     alert(houseRent.currentAmount()); // $90,000
     houseRent.changeBy(20000) // TypeError: undefined is         
     not a function

    Using closures to namespace private functions keeps more general namespaces clean, preventing naming collisions. Neither the rent variable nor the changeBy function is available outside of houseRent. However, raise, lower and currentAmount all have access to them and can be called on houseRent.

    How Closures Promote Functional Programming

    Closures are very important in functional programming as they are used for two concepts — partial application and currying.

    Currying is simply a way of constructing functions that allow for the partial application of a function’s arguments. If an application is the process of applying a function to its arguments to produce a return value, then the partial application is the process of applying a function to some of its arguments. In other words, a function that takes another function with multiple parameters and returns a function with fewer parameters. Partial application fixes one or more arguments inside the returned function. The returned function then takes the remaining parameters as arguments to complete the function application:

        partialApplication(targetFunction: Function, ...fixedArgs: Any[]) =>
          functionWithFewerParams(...remainingArgs: Any[])

    In summary, what the signature above does is to take a function that takes any number of arguments, followed by arguments we want to apply to the function partially, and then return a function that will take the remaining arguments.

    Let’s see how some common array methods are created to understand these concepts better:

    Map

    Map method iterates over a list of items in an array and applies a transformation to it. The transformation is applied via a callback method:

     Array.prototype.map = function(callback) {  
       arr = [];
       for (var i = 0; i < this.length; i++)
         arr.push(callback(this[i],i,this));
       return arr;
     };

    Filter

    The filter method has the same structure as the map, but instead of applying transformations, it applies a filter. Each of the items that match the filter is what gets returned:

     Array.prototype.filter = function(callback, context) {
       arr = [];
       for (var i = 0; i < this.length; i++) {
         if(callback.call(context, this[i], i, this))
         arr.push(this[i]);
       }
       return arr;
     };

    Conclusion

    Next time when you see Closures in your code or some code you saw on Github, you can easily identify them and see what they are doing. There is nothing more to it but practice; now that you have the basic knowledge, dig some existing code and try to identify the Closures and why they exist there.

    Chris Nwamba

    107 posts

    JavaScript Preacher. Building the web with the JS community.