Producing functions from functions

Let's finish this section on functional aspects by looking at a quintessential functional programming tool: Higher Order Functions (HOFs): functions that produce functions as results! In later chapters, we'll actually meet more usages of HOFs; here, let's work out a simple example.

The following example is taken from my previous book for Packt, Mastering JavaScript Functional Programming. Chapter 2, Thinking Functionally - A First Example, and Chapter 6, Producing Functions - Higher-Order Functions will be of particular interest with regard to HOFs. See more at www.packtpub.com/web-development/mastering-javascript-functional-programming.

Suppose you have developed an e-commerce site. The user selects products, adds them to his/her shopping cart, and at the end clicks on a BILL ME button so that his/her credit card will be charged. However, if the user were to click twice or more, he/she would be billed several times rather than once. Your application might have something along these lines in its HTML:

<button id="billBtn" onclick="billUser(sales, data)">Bill me</button>

Somewhere among your scripts, there would be some code like the following. I'm not including data type declarations because they are not relevant to our code; we don't really know or care what the arguments to billUser() would be:

function billUser(sales, data) {
window.alert("Billing the user...");
// actually bill the user
}

Now, what could you do in order to avoid repeated clicks on the button? There are several not-quite-so-good solutions, such as the following:

  • Do nothing, just warn the user, and hope they pay attention!
  • Use a global flag to signal the fact that the user clicked once.
  • Remove the onclick handler from the button after the user clicks.
  • Change the onclick handler to something else that won't bill the user.

However, all of these solutions are somewhat lacking, depend on global objects, need you to mess with the billing function, are tightly linked with the user view, and so on. Since requiring that some functions are executed only once isn't such an outlandish requirement, let's specify the following:

  • The original function should be unchanged and do its thing—nothing more
  • We want a new function that will call the original one, but only once
  • We want a general solution so that we can apply it in different situations

We will write a function, once(), that will take a function as its argument and produce a new function, but that will do its thing only once. The logic is not long, but study it carefully:

// Source file: src/functional_code.js

const once = fn => {
let done = false;
return (...args) => {
if (!done) {
done = true;
fn(...args);
}
};
};

Some analysis of our new function is as follows:

  • The definition shows that once() takes a generic function (fn()) as an argument
  • The return statement shows that once() returns another function
  • We are using the spread operator to deal with functions with any number of arguments
  • We are using a closure for the done variable, which remembers whether fn() was invoked or not
I left out type definitions for clarity, but in the source code provided with this book, full definitions are provided. Can you work them out on your own? A tip: the output of the once() function should be the same type as the input to it.

With this new function, you could have coded the button as follows. When the user clicks on the button, the function that will get called with (sales, data) as arguments isn't billUser(), but rather the result of having applied once() to billUser()—and that would have resulted in a new function that would have called billUser() only once:

<button id="billButton" onclick="once(billUser)(sales, data)">
Bill me
</button>;

This is the concept of a higher order function: a function that receives functions as arguments and produces a new function as a result. Usually, there are three kinds of possible transformations that we could desire:

  • Wrapping functions: We do this so that they keep their original functionality, but add some new feature; for example, we could add logging or timing so that the original function still does its thing, but log its parameters or produce timing information
  • Altering functions: We do this so that they will differ in some key point with the original version; this is what we did with once(), which produces a new version of a function that runs only a single time
  • Other changes: These changes include turning a function into a promise (we'll see this when we get to Node, in the Using Promises instead of error first callbacks section of Chapter 3, Developing with Node) and more

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.174.168