© Anto Aravinth, Srikanth Machiraju 2018
Anto Aravinth and Srikanth MachirajuBeginning Functional JavaScripthttps://doi.org/10.1007/978-1-4842-4087-8_4

4. Closures and Higher Order Functions

Anto Aravinth1  and Srikanth Machiraju2
(1)
Chennai, Tamil Nadu, India
(2)
Hyderabad, Andhra Pradesh, India
 

In the previous chapter we saw how higher order functions help developers create abstraction over common problems. It’s a very powerful concept, as we learned. We have created our sortBy higher order function to showcase a valid and relevant example of the use case. Even though the sortBy function is working on the basis of higher order functions (which is again the concept of passing functions as arguments to the other functions), it has something to do with yet another concept called closures in JavaScript.

We need to understand closures in the JavaScript world before we go further in our journey of functional programming techniques. That’s where this chapter comes into the picture. In this chapter we are going to discuss in detail what is meant by closures and at the same time continue our journey of writing useful and real-world higher order functions. The concept of closures has to do with scopes in JavaScript, so let’s get started with closures in the next section.

Note

The chapter examples and library source code are in branch chap04. The repo’s URL is https://github.com/antoaravinth/functional-es8.git

Once you check out the code, please check out branch chap04:

...

git checkout -b chap04 origin/chap04

...

For running the codes, as before run:

...

npm run playground

...

Understanding Closures

In this section we are going to explain what we mean by closures with a simple example and then move on to our sortBy function by unwrapping how it works with closures.

What Are Closures?

Simply put, a closure is an inner function. So what is an inner function? It is just a function within another function, something like the following:
function outer() {
   function inner() {
   }
}

Yes, that’s exactly what a closure is. The function inner is called a closure function . Closure is powerful because of its access to the scope chains (or scope levels). We will discuss scope chains in this section.

Note

Scope chains and scope levels mean the same, so they are used interchangeably in this chapter.

Technically the closure has access to three scopes:
  1. 1.

    Variables that are declared in its own declaration.

     
  2. 2.

    Access to the global variables.

     
  3. 3.

    Access to the outer function’s variable (interesting).

     
Let’s talk about these three points separately with a simple example. Consider the following code snippet:
function outer() {
   function inner() {
        let a = 5;
        console.log(a)
   }
   inner() //call the inner function.
}

What will be printed to the console when the inner function gets called? The value will be 5. This is mainly due to the first point. A closure function can access all the variables declared in its own declaration (see Point 1). No rocket science here!

Note

A strong takeaway from the preceding code snippet is that the inner function won’t be visible outside the outer function! Go ahead and test it.

Now modify the preceding code snippet to the following:
let global = "global"
function outer() {
   function inner() {
        let a = 5;
        console.log(global)
   }
   inner() //call the inner function.
}

Now when the inner function is executed, it does print the value global. Thus closures can access the global variable (see Point 2).

Points 1 and 2 are now clear with the example. The third point is very interesting, and the claim can be seen in the following code:
let global = "global"
function outer() {
   let outer = "outer"
   function inner() {
        let a = 5;
        console.log(outer)
   }
   inner() //call the inner function.
}

Now when the inner function executes, it does print the value outer. This looks reasonable, but it is a very important property of a closure. Closure has access to the outer function’s variable(s). Here outer function means the function that encloses the closure function. This property is what makes the closures so powerful!

Note

Closure also has access to the enclosing function parameters. Try adding a paramater to our outer function and try to access it from the inner function. We will wait here until you are done with this small exercise.

Remembering Where It Is Born

In the previous section we saw what a closure is. Now we will be seeing a slightly complicated example, which explains yet another important concept in closure: closure remembering its context.

Take a look at the following code:
var fn = (arg) => {
        let outer = "Visible"
        let innerFn = () => {
                console.log(outer)
                console.log(arg)
        }
        return innerFn
}

The code is simple. The innerFn is a closure function to fn and fn returns the innerFn when called. There is nothing fancy here.

Let’s play around with fn:
var closureFn = fn(5);
closureFn()
will print the following:
Visible
5

How does calling closureFn print Visible and 5 to the console? What is happening behind the scenes? Let’s break it down.

There are two steps happening in this case:
  1. 1.

    When this line is called:

     
var closureFn = fn(5);
our fn gets called with argument 5. As per our fn definition, it returns the innerFn.
  1. 2.

    This where interesting things happen. When innerFn is returned, the JavaScript execution engine sees innerFn as a closure and sets its scope accordingly. As we saw in the previous section, closures will have access to the three scope levels. All these three scope levels are set (arg, outer values will be set in scope level of innerFn) when the innerFn is returned. The returned function reference is stored in closureFn. Thus closureFn will have arg, outer values when called via scope chains.

     
  2. 3.

    When we finally call the closureFn:

     
closureFn()
it prints:
Visible
5

As now you can guess, closureFn remembers its context (the scopes; i.e., outer and arg) when it is born in the second step. Thus the calls to console.log print appropriately.

You might be wondering what is the use case of closure?. We have already seen it in action in our sortBy function. Let’s quickly revisit it.

Revisiting sortBy Function

Recall the sortBy function that we defined and used in the previous chapter:
const sortBy = (property) => {
    return (a,b) => {
        var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
        return result;
    }
}

When we called the sortBy function like this:

sortBy("firstname")sortBy returned a new function that takes two arguments, like this:
(a,b) => { /* implementation */ }
Now we are comfortable with closures and we are aware that the returned function will have access to the sortBy function argument property. Because this function will be returned only when sortBy is called, the property argument is linked with a value; hence the returned function will carry this context throughout its life:
//scope it carries via closure
property = "passedValue"
(a,b) => { /* implementation */ }

Now because the returned function carries the value of property in its context, it will use the returned value where it is appropriate and when it is needed. With that explanation in place, we can fully understand closures and higher order functions that allow us to write a function like sortBy that is going to abstract away the inner details. Moving ahead to our functional world.

That’s a lot to take in for this section; in the next section we continue our journey of writing more abstract functions using closures and higher order functions.

Higher Order Functions in the Real World (Continued)

With our understanding of closures in place, we can go ahead and implement some useful higher order functions that are used in the real world.

tap Function

Because we are going to deal with lots of functions in the programming world, we need a way to debug what is happening between them. As we have seen in previous chapters, we are designing the functions, which take arguments and return another function, which again takes a few arguments, and so on.

Let’s design a simple function called tap:
const tap = (value) =>
  (fn) => (
    typeof(fn) === 'function' && fn(value),
    console.log(value)
  )

Here the tap function takes a value and returns a function that has the closure over value and it will be executed.

Note

In JavaScript, (exp1,exp2) means it will execute the two arguments and return the result of the second expression, which is exp2. In our preceding example, the syntax will call the function fn and also print the value to the console.

Let’s play around with the tap function:
tap("fun")((it) => console.log("value is ",it))
=>value is fun
=>fun

As you can see in this example, the value value is fun gets printed and then the value fun is printed. This seems easy and straightforward.

So where can the tap function be used? Imagine you are iterating an array that has data come from a server. You feel that the data are wrong, so you want to debug and see what the array really contains, while iterating. How will you do that? This is where the tap function comes into the picture. For the current scenario, we can do this:
forEach([1,2,3], (a) =>
   tap(a)(() =>
     {
       console.log(a)
     }
   )
)

This prints the value as expected, providing a simple yet powerful function in our toolkit.

unary Function

There is a default method in the array prototype called map . Don’t worry; we are going to discover numerous functions for arrays in the next chapter, where we will be seeing how to create our own map, too. For now, map is a function, which is very similar to the forEach function we have already defined. The only difference is that map returns the result of the callback function.

To get the gist of it, let’s say we want to double the array and get back the result; using the map function, we can do that like this:
[1, 2, 3].map((a) => { return a * a })
=>[1, 4, 9]
The interesting point to note here is that map calls the function with three arguments, which are element, index, and arr. Imagine we want to parse the array of strings to the array of int; we have a built-in function called parseInt that takes two argument parses and radixes and converts the passed parse into a number if possible. If we pass the parseInt to our map function, map will pass the index value to the radix argument of parseInt, which will result in unexpected behavior.
['1', '2', '3'].map(parseInt)
=>[1, NaN, NaN]

Oops! As you can see in this result, the array [1, NaN, NaN] is not what we expect. Here we need to convert the parseInt function to another function that will be expecting only one argument. How can we achieve that? Meet our next friend, the unary function. The task of the unary function is to take the given function with n arguments and convert it into a single argument.

Our unary function looks like the following:
const unary = (fn) =>
  fn.length === 1
    ? fn
    : (arg) => fn(arg)

We are checking if the passed fn has an argument list of size 1 (which we can find via the length property); if so, we are not going to do anything. If not, we return a new function , which takes only one argument arg and calls the function with that argument.

To see our unary function in action, we can rerun our problem with unary:
['1', '2', '3'].map(unary(parseInt))
=>[1, 2, 3]

Here our unary function returns a new function (a clone of parseInt), which is going to take only one argument. Thus the map function passing index, arr argument, becomes unaffected as we are getting back the expected result.

Note

There are also functions like binary and others that will convert the function to accept corresponding arguments.

The next two functions that we are going to see are special higher order functions that will allow the developer to control the number of times the function is getting called. They have a lot of use cases in the real world.

once Function

There are a lot of situations in which we need to run a given function only once. This scenario occurs for JavaScript developers in their day-to-day life, as they want to set up a third-party library only once, initiate the payment set up only once, do a bank payment request only once, and so on. These are common cases that developers face.

In this section we are going to write a higher order function called once, which will allow the developer to run the given function only once. Again the point to note here is that we have to keep on abstracting away our day-to-day activities into our functional toolkits.
const once = (fn) => {
  let done = false;
  return function () {
    return done ? undefined : ((done = true), fn.apply(this, arguments))
  }
}

This once function takes an argument fn and returns the result of it by calling it with the apply method (a note on the apply method is given later). The important point to note here is that we have declared a variable called done and set it to false initially. The returned function will have a closure scope over it; hence it will access it to check if done is true, if return undefined else set done to true (thus preventing the next execution), and calling the function with necessary arguments.

Note

The apply function will allow us to set the context for the function and also pass on the arguments for the given function. You can find more about it at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply .

With the once function in place, we can do a quick check of it.
var doPayment = once(() => {
   console.log("Payment is done")
})
doPayment()
=>Payment is done
//oops bad, we are doing second time!
doPayment()
=>undefined!

This code snippet showcases that the doPayment function that is wrapped over once will be executed only once regardless of how many times we call them. The once function is a simple but effective function in our toolkit.

memoize Function

Before we close this section, let’s take a look at the function called memoize . We know that the pure function is all about working on its argument and nothing else. It does not depend on the outside world for anything. The results of the pure function are purely based on its argument. Imagine that we have a pure function called factorial, which calculates the factorial for a given number. The function looks like this:
var factorial = (n) => {
  if (n === 0) {
    return 1;
  }
  // This is it! Recursion!!
  return n * factorial(n - 1);
}
You can quickly check that factorial function with a few inputs :
factorial(2)
=>2
factorial(3)
=>6

Nothing fancy here. We knew, though, that the factorial of the value 2 is 2, 3 is 6, and so on, mainly because we know the factorial function does work, but only based on its argument and nothing else. This question then arises here: Why can’t we store back the result for each input (some sort of an object) and give back the output if the input is already present in the object? Moreover for calculating the factorial for 3, we need to calculate the factorial for 2, so why can’t we reuse those calculations in our function? Well, that’s exactly what the memoize function is going to do. The memoize function is a special higher order function that allows the function to remember or memorize its result.

Let’s see how we can implement such a function in JavaScript. It is as simple as it looks here:
const memoized = (fn) => {
  const lookupTable = {};
  return (arg) => lookupTable[arg] || (lookupTable[arg] = fn(arg));
}
Here we have a local variable called lookupTable that will be in the closure context for the returned function. This will take the argument and check if that argument is in the lookupTable:
. . lookupTable[arg] . .
If so, return the value; otherwise update the object with new input as a key and the result from fn as its value:
(lookupTable[arg] = fn(arg))
Perfect. Now we can go and wrap our factorial function into a memoize function to keep remembering its output:
let fastFactorial = memoized((n) => {
  if (n === 0) {
    return 1;
  }
  // This is it! Recursion!!
  return n * fastFactorial(n - 1);
})
Now go and call fastFactorial:
fastFactorial(5)
=>120
=>lookupTable will be like: Object {0: 1, 1: 1, 2: 2, 3: 6, 4: 24, 5: 120}
fastFactorial(3)
=>6 //returned from lookupTable
fastFactorial(7)
=> 5040
=>lookupTable will be like: Object {0: 1, 1: 1, 2: 2, 3: 6, 4: 24, 5: 120, 6: 720, 7: 5040}

It is going to work the same way, but now much faster than before. While running fastFactorial, I would like you to inspect the lookupTable object and how it helps in speeding things up as shown in the preceding snippet. That is the beauty of the higher order function: closure and pure functions in action!

Note

Our memoized function is written for functions that take only one argument. Can you come up with a solution for all functions with n number of arguments?

We have abstracted away many common problems into higher order functions that allowed us to write a solution with elegance and ease.

assign function

JavaScript (JS) objects are mutable, which means the state of the object can be changed after it is created. Often, you will come across a scenario in which you have to merge objects to form a new object. Consider the following objects:
var a = {  name: "srikanth" };
var b = {  age: 30 };
var c = {  sex: 'M' };
What if I want to merge all objects to create a new object? Let us go ahead and write the relevant function.
function objectAssign(target, source) {
    var to = {};
    for (var i = 0; i < arguments.length; i += 1) {
      var from = arguments[i];
      var keys = Object.keys(from);
      for (var j = 0; j < keys.length; j += 1) {
        to[keys[j]] = from[keys[j]];
      }
    }
    return to;
  }
arguments is a special variable available to every JS function. JS functions allow you to send any number of arguments to a function, which means that if a function is declared with two arguments, JS allows you to send more than two arguments. Object.keys is an inbuilt method that gives you the property names of every object, in our case, the name, age, and sex. The following usage shows how we abstracted the functionality to merge any number of JS objects into one object.
var customObjectAssign = objectAssign(a, b, c);
//prints { name: 'srikanth', age: 30, sex: 'M' }
However, if you’re following ES6 standards, you may not have to write a new function. The following function also does the same.
// ES6 Object.Assign
var nativeObjectAssign = Object.assign(a, b, c);
//prints { name: 'srikanth', age: 30, sex: 'M' }
Note that when we use Object.assign to merge objects a, b, and c, even object a is changed. This does not occur with our custom implementation. That is because object a is considered to be the target object we merge into. Because the objects are mutable, a is now updated accordingly. If you require the preceding behavior, you can do this:
var nativeObjectAssign = Object.assign({}, a, b, c);

Object a will be intact with the preceding usage, because all the objects are merged into an empty object.

Let me show you another new addition to ES6, Object.entries. Suppose you have an object such as the following:
var book = {
        "id": 111,
        "title": "C# 6.0",
        "author": "ANDREW TROELSEN",
        "rating": [4.7],
        "reviews": [{good : 4 , excellent : 12}]    };
If you’re only interested in the title property, the following function can help you convert that property into an array of strings.
console.log(Object.entries(book)[1]);
//prints Array ["title", "C# 6.0"]

What if you do not want to upgrade to ES6 and yet you’re interested in getting object entries? The only way is to implement a functional method that does the same, such as we did earlier. Are you up for the challenge? If yes, I will leave that as an exercise for you.

We have now abstracted away many common problems into higher-order functions that allowed us to write an elegant solution with ease.

Summary

We started this chapter with a set of questions about what a function can see. By starting small and building up examples, we showed how closures make the function remember the context in which it is born. With this understanding in place, we implemented few more higher order functions that are used in the day-to-day life of a JavaScript programmer. Throughout we have seen how to abstract away common problems into a specific function and reuse it. Now we understand the importance of closures, higher order functions, abstraction, and pure functions. In the next chapter we are going to continue building the higher order functions, but with respect to arrays.