© Anto Aravinth 2017

Anto Aravinth, Beginning Functional JavaScript, 10.1007/978-1-4842-2656-8_4

4. Closures and Higher-Order Functions

Anto Aravinth

(1)Chennai, Tamil Nadu, India

Note

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

Once checkout the code, please checkout branch chap04:

...

git checkout -b chap04 origin/chap04

...

For running the codes, as before run:

...

npm run playground

...

In the previous chapter we saw how higher-order functions help us as a developer to create abstraction over the 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.

Closures is a concept that we need to understand in the JavaScript world before we go further in our journey of functional programming techniques. And 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.

Understanding Closures

In this section we are going to see 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 closure is an inner function. So what is an inner function? Well, its a function within an another function. Something like the following:

function outer() {
   function inner() {
   }
}

Yes, thats exactly what a closure is. The function inner is called a closure function. The reason why closure is so powerful is because of its access to the scope chains (or scope levels). We will be talking about scope chains in this section.

Note

Scope chains and Scope levels are used interchangeably in this chapter.

Technically the closure has access to three scopes:

  1. Variables that are declared in its own declaration

  2. Access to the global variables.

  3. Access to the outer function's variable (interesting!)

Lets talk about theses three points separately with simple example. Consider the following snippet:

function outer() {
   function inner() {
        let a = 5;
        console.log(a)
   }
   inner() //call the inner function.
}

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

Note

A strong take away from the above code snippet is that, inner function won't be visible outside outer function! Go ahead and test it.

Now modifying the above 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 inner function 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 3rd point is very interesting, 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 inner function executed, it does print the value outer. This looks reasonable, but its a very important property of a closure.

Closure has the access to the outer function's variable(s). Here outer function mean, the function which encloses the closure function.

This property is what make 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 inner function. I will wait here till your 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 slightly a 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. Nothing fancy here.

Lets play around with fn:

var closureFn = fn(5);
closureFn()

will print the following:

Visible
5

How does calling closureFn prints Visible and 5 to the console? What is happening behind the scenes? Lets break it down.

There are two steps happening in this case:

  1. When the below line is called:

    var closureFn = fn(5);

    our fn gets called with argument 5. As per our fn definition, it returns the innerFn.

  2. This where interesting things happens. When innerFn is returned, the javascript execution engine sees innerFn as a closure and sets its scope accordingly. As we have seen in the previous section, closures will have access to the 3 scope level. All these 3 scope level 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 remember arg, outer values when called via scope chains!

  3. When we finally call the closureFn:

    closureFn()

    it prints:

    Visible
    5

As now you can guess it out, closureFn remembers its context (the scopes i.e outer and arg) when it 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 them.

Revisiting sortBy Function

Let’s quickly revisit the sortBy function that we have 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 sortBy function like this:

sortBy("firstname")

this is what happened:

sortBy returned a new function that takes two argument 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. Since this function will be returned only when sortBy is called, the property argument is revolved 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 since 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! Now with that explanation in place, we can fully understand how 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 will be continuing 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 will go ahead and implement some useful higher-order functions that are used in the real world.

tap Function

Since we are going to deal with lots of functions in functional 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 returns another function, which again takes a few arguments, etc., 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 the (exp1,exp2) means it will execute the two arguments and return the result of the second expression, which is exp2. In our above example, the syntax will call the function fn and also prints 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 the above example, the value ‘value is fun’ gets printed and then the value ‘fun’ is printed. Easy and straightforward.

So where can the tap function be used? Let’s imagine you are iterating an array that has data come from a server. You are iterating the array and you feel that the data is wrong so you want to debug and see what the array really contains, while iterating. How will you do that? Nope, don't be imperative, let’s be functional. 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)
     }
   )
)

which does print the value as expected. A simple but 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 a whole lot of functional functions for array 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 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; then using the map function, we can do like this:

[1, 2, 3].map((a) => { return a * a })
=>[1, 4, 9]

The interesting point to note over here is that the 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 an in-built 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 the above 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, unary function. The task of unary function is to take the given function with n argument 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 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, etc., which will convert the function to accept corresponding arguments.

The next two functions that we are going to see are special higher-order functions, which 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 to 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, etc. These are common cases that the 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 over here is that we have 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))
  }
}

The above once function takes an argument fn and returns the result of it by calling it with the apply method (note on the apply method is down below). 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 next time 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 over here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

With the once function in place, we can go and 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!

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

Memoize Function

Before we close this exciting section, let’s see my favorite function called memoize. We know that the pure function is all about working on its argument and nothing else. They don't depend on the outside world for anything. The results of the pure function are purely based on its argument only. Imagine that we have a pure function called factorial, which calculates the factorial for the given number. The function looks like the following:

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. But we knew 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! So the question 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? And 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? Yup, that's exactly what the memoize function is going to do. The memoize 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. No worries; it is as simple as it looks - like the following:

const memoized = (fn) => {
  const lookupTable = {};


  return (arg) => lookupTable[arg] || (lookupTable[arg] = fn(arg));
}

Here in the above function 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 else, update the object with new input as key and 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 the fastFactorial, I would like you to inspect the lookupTable object and how it helps in speeding things up as shown in the above snippet!

That is the beauty about the higher-order function – closure and pure functions in action!

Note

That our memoized function is written for the functions that take up 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 the solution with elegant and ease.

Summary

We started this chapter with a set of questions on what a function can see. By starting small and building up examples, we understand how closures make the function to remember the context where it is born. With this understanding in place, we went ahead and 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 the common problems into a specific function and reuse them! 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!