JavaScript - - By M. David Green

Higher-Order Functions in JavaScript

An Introduction To Functional Javascript

One of the characteristics of JavaScript that makes it well-suited for functional programming is the fact that it can accept higher-order functions. A higher-order function is a function that can take another function as an argument, or that returns a function as a result.

First Class Functions

You may have heard it said that JavaScript treats functions as first-class citizens. What this means is that functions in JavaScript are treated as objects. They have the type Object, they can be assigned as the value of a variable, and they can be passed and returned just like any other reference variable.

This native ability gives JavaScript special powers when it comes to functional programming. Because functions are objects, the language supports a very natural approach to functional programming. In fact, it’s so natural, that I’ll bet you’ve been using it without even thinking about it.

Taking Functions as Arguments

If you’ve done much web-based JavaScript programming or front-end development, you’ve probably come across functions that use a callback. A callback is a function that gets executed at the end of an operation, once all of the other operations of been completed. Usually this callback function is passed in as the last argument in the function. Frequently, it’s defined inline as an anonymous function.

Since JavaScript is single-threaded, meaning that only one operation happens at a time, each operation that’s going to happen is queued along this single thread. The strategy of passing in a function to be executed after the rest of the parent function’s operations are complete is one of the basic characteristics of languages that support higher-order functions. It allows for asynchronous behavior, so a script can continue executing while waiting for a result. The ability to pass a callback function is critical when dealing with resources that may return a result after an undetermined period of time.

This is very useful in a web programming environment, where a script may send an Ajax request off to a server, and then need to handle the response whenever it arrives, with no knowledge in advance of network latency or processing time on the server. Node.js frequently uses callbacks to make the most efficient use of server resources. This approach is also useful in the case of an app that waits for user input before performing a function.

For example, consider this snippet of simple JavaScript that adds an event listener to a button.

<button id="clicker">So Clickable</button>

document.getElementById("clicker").addEventListener("click", function() {
  alert("you triggered " + this.id);
});

This script uses an anonymous inline function to display an alert. But it could just as easily have used a separately defined function, and passed that named function to the addEventListener method

var proveIt = function() {
  alert("you triggered " + this.id);
};

document.getElementById("clicker").addEventListener("click", proveIt);

Note that we passed proveIt and not proveIt() to our addEventListener function. When you pass a function by name without parentheses, you are passing the function object itself. When you pass it with parentheses, you are passing the result of executing that function.

Our little proveIt() function is structurally independent of the code around it, always returning the id of whatever element was triggered. This bit of code could exist in any context in which you wanted to display an alert with the id of an element, and could be called with any event listener.

The ability to replace an inline function with a separately defined and named function opens up a world of possibilities. As we try to develop pure functions that don’t alter external data, and return the same result for the same input every time, we now have one of the essential tools to help us develop a library of small, targeted functions that can be used generically in any application.

Returning Functions as Results

In addition to taking functions as arguments, JavaScript allows functions to return other functions as a result. This makes perfect sense, since functions are simply objects, they can be returned the same as any other value.

But what does it mean to return a function as a result? Defining a function as the return value of another function allows you to create functions that can be used as templates to create new functions. That opens the door to another world of functional JavaScript magic.

For example, imagine you’ve gotten tired of reading all of these articles about the specialness of Millennials, and you decide you want to replace the word Millennials with the phrase Snake People every time it occurs. Your impulse might be simply to write a function that performed that text replacement on any text you passed to it:

var snakify = function(text) {
  return text.replace(/millenials/ig, "Snake People");
};
console.log(snakify("The Millenials are always up to something."));
// The Snake People are always up to something.

That works, but it’s pretty specific to this one situation. You’re also tired of hearing about the Baby Boomers. You’d like to make a custom function for them as well. But even with such a simple function, you don’t want to have to repeat the code that you’ve written:

var hippify = function(text) {
  return text.replace(/baby boomers/ig, "Aging Hippies");
};
console.log(hippify("The Baby Boomers just look the other way."));
// The Aging Hippies just look the other way.

But what if a you decided that you wanted to do something fancier to preserve case in the original string? You would have to modify both of your new functions to do this. That’s a hassle, and it makes your code more brittle and harder to read.

What you really want is the flexibility to be able to replace any term with any other term in a template function, and define that behavior as a foundational function from which you could spawn a whole race of custom functions.

With the ability to return functions instead of values, JavaScript offers up ways to make that scenario much more convenient:

var attitude = function(original, replacement, source) {
  return function(source) {
    return source.replace(original, replacement);
  };
};

var snakify = attitude(/millenials/ig, "Snake People");
var hippify = attitude(/baby boomers/ig, "Aging Hippies");

console.log(snakify("The Millenials are always up to something."));
// The Snake People are always up to something.
console.log(hippify("The Baby Boomers just look the other way."));
// The Aging Hippies just look the other way.

What we’ve done is isolate the code that does the actual work into a versatile and extensible attitude function that encapsulates all of the work needed to modify any input string using an original phrase and a replacement phrase with some attitude.

Defining a new function as a reference to the attitude function, pre-populated with the first two arguments it takes, allows the new function to take whatever argument you pass it and use that as the source text in the internal function returned by the attutide function.

We’re taking advantage here of the fact that JavaScript functions don’t care whether they are passed the same number of arguments as they were originally defined to take. If an argument is missing, the function will just treat the missing arguments as undefined.

On the other hand, that additional argument can be passed in later when the function being called has been defined in the way we just demonstrated, as a reference to a function returned from another function with an argument (or more) left undefined.

Go over that a few times if you need to, so you fully understand what’s happening. We’re creating a template function that returns another function. Then we’re defining that newly returned function, minus one attribute, as a custom implementation of the template function. All the functions created this way will inherit the same working code from the template function, but can be predefined with different default arguments.

It’s What You’re Already Doing

Higher-order functions are so basic to the way JavaScript works, you’re already using them. Every time you pass an anonymous function or a callback, you’re actually taking the value that the passed function returns, and using that as an argument for another function.

The ability of functions to return other functions extends the convenience of JavaScript, allowing us to create custom named functions to perform specialized tasks with shared template code. Each of these little functions can inherit any improvements made in the original code down the road, which helps us avoid code duplication, and keeps our source clean and readable.

As an added bonus, if you make sure your functions are pure, so that they don’t alter external values and they always return the same value for any given input, you put yourself in a good position to create test suites to verify that your code changes don’t break anything you’re relying on when you update your template functions.

Start thinking about ways you can take advantage of this approach in your own projects. One of the great things about JavaScript is that you can mix functional techniques right in with the code you’re already familiar with. Try some experiments. You may be surprised how easily a little work with higher-order functions can improve your code.

Sponsors