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?
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.
- 1.
Variables that are declared in its own declaration.
- 2.
Access to the global variables.
- 3.
Access to the outer function’s variable (interesting).
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 when the inner function is executed, it does print the value global. Thus closures can access the global variable (see Point 2).
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.
The code is simple. The innerFn is a closure function to fn and fn returns the innerFn when called. There is nothing fancy here.
How does calling closureFn print Visible and 5 to the console? What is happening behind the scenes? Let’s break it down.
- 1.
When this line is called:
- 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.
- 3.
When we finally call the closureFn:
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
When we called the sortBy function like this:
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.
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.
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.
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.
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.
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.
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.
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 .
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
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.
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
Object a will be intact with the preceding usage, because all the objects are merged into an empty object.
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.