UnderStanding Higher Order Functions in JavaScript

January 25, 2024
·
views
·
likes

Functions as First Class Citizens in JavaScript

Functions are defined as first class citizens or first class objects in JavaScript because functions are treated like variables.

This means that functions in JavaScript can be:

It is essential to understand how functions are treated in JavaScript, as they serve as a building block to understanding higher order and callback functions in JavaScript and how they work.

What are Higher Order Functions?

Higher order functions are functions that take functions as arguments and also return a function as a value.

There are a lot of built-in higher order functions provided in JavaScript. We'll take a look at some and take advantage of how functions are treated as first class citizens. We'll also create our own higher order functions.

First, let's take a look at some examples of built-in higher order functions.

Array Methods

Array methods are usually the first introduction of higher order functions a developer will have when learning JavaScript. These include, but are not limited to, the map, filter, forEach, find, findIndex, some, and every array methods provided by JavaScript.

These array methods or functions have a lot in common, but one of the most common feature is that they all accept a function as an argument. Below is a code snippet that demonstrates how the forEach array method works:

const people = [
  { firstName: "Jack", year: 1988 },
  { name: "Kait", year: 1986 },
  { name: "Irv", year: 1970 },
  { name: "Lux", year: 2015 },
];
 
people.forEach(function (person) {
  console.log(person);
});

From the code sample above, we can see that the forEach method accepts a function as an argument which it calls on every iteration on the array. Therefore the forEach array method is a higher order function.

Timer Events

Another set of commonly used built-in higher order functions are the setInterval and setTimeout functions, known as timer events in JavaScript.

Each function accepts a function as one of its arguments and uses it to create a timed event.

Take a look at the code sample below to see how setTimeout works:

setTimeout(function () {
  console.log("This is a higher order function");
}, 1000);
 
// Output: "This is a higher order function" after 1000ms / 1 second

The code snippet above is the most basic example of how a setTimeout function works. It accepts a function and a time duration in milliseconds and executes the function after the provided duration has passed.

From the example above, This is a higher order function is printed to the console after 1000 ms, or one second.

setInterval(function () {
  console.log("This is a higher order function");
}, 1000);
 
// Output: "This is a higher order function" after every 1000ms / 1 second

The setInterval function is similar to the setTimeout function, just like the array methods – although it functions differently. But we can see a common pattern: it also accepts a function as one of its parameters.

Unlike setTimeout (that executes the function after the provided duration has passed), setInterval executes the function over and over again every 1000ms or 1 second.

How to Create and Use a Higher Order Function

Higher order functions are not limited to the built-in ones provided by JavaScript.

Since functions in JavaScript are treated as first class objects, we can take advantage of this behavior and build highly performant and reusable functions.

In the examples below, we'll build a couple of functions. They'll accept the name of a customer and a greeting, and then print that info to the console.

First, here is a simple function that does both of those things:

function greetCustomer(firstName, lastName, salutation) {
  const fullName = `${firstName} ${lastName}`;
 
  console.log(`${salutation} ${fullName}`);
}
 
greetCustomer("Franklin", "Okolie", "Good Day");
 
// Output: "Good Day Franklin Okolie"

greetCustomer accepts 3 arguments: a first name, a last name, and a salutation. Then it prints a greeting to the customer to the console.

But there is a problem with this function – it's doing two things: composing the full name of the customer and also printing the greeting.

This is not a best practice, as functions should do only one thing and do it well. So we are going to refactor our code.

Another function should compose the customer's name so that the greetCustomer function only has to print the greeting to the console. So let's write a function that handles that:

function composeName(firstName, lastName) {
  const fullName = `${firstName} ${lastName}`;
 
  return fullName;
}

Now that we have a function that combines the customer's first and last names, we can use that function in greetCustomer:

function greetCustomer(composerFunc, firstName, lastName, salutation) {
  const fullName = composerFunc(firstName, lastName);
 
  console.log(`${salutation} ${fullName}`);
}
 
greetCustomer(composeName, "Franklin", "Okolie", "Good Day");
 
// Output: "Good Day Franklin Okolie"

Now this looks cleaner, and each function does just one thing. The greetCustomer function now accept 4 arguments, and since one of those arguments is a function, it's now a higher order function.

You might have wondered earlier, how is a function being invoked inside of another function, and why?

Now we'll take a deep dive into function invocation and answer both of those questions.

Returning a Function as a Value

Remember that higher order functions either take a function as a parameter and/or return a function as a value.

Let's refactor the greetCustomer function to use fewer arguments and return a function:

function getGreetingsDetails(composerFunc, salutation) {
  return function greetCustomer(firstName, lastName) {
    const fullName = composerFunc(firstName, lastName);
 
    console.log(`${salutation} ${fullName}`);
  };

The last version of greetCustomer accepted too many arguments. Four arguments isn't a lot, but it would still be frustrating if you messed up the order of the arguments. Generally, the fewer arguments you have, the better.

So in the example above, we have a function called getGreetingDetails which accepts composerFunc and salutation on behalf of the inner greetCustomer function. It then returns the inner greetCustomer function, which itself accepts firstName and lastName as arguments.

By doing this, greetCustomer has fewer arguments overall.

And with that, let's take a look at how to use the getGreetingDetails function:

const greet = getGreetingsDetails(composeName, "Happy New Year!");
 
greet("Quincy", "Larson");
 
// Output: "Happy New Year Quincy Larson"
LikeButton
Footer