JavaScript Higher-Order Functions: A Guide with Examples

Functions are one of the fundamental building blocks in Javascript. A function is a set of statements that perform a task or calculate a value. They come in handy as they simplify our program’s structure, readability, and reusability by abstracting the implementation of a particular algorithm. As a functional language, Javascript supports the concept of high-order functions to abstract the purpose of a particular function.

A high-order function in Javascript is a function that,

  • Takes a function as an argument. Or,
  • Returns a function as a value after it is done with computation. Or,
  • Performs both operations – takes a function as an argument and returns a function.

Why should a function take another function as a parameter? Why would a function return another function as a value? To answer all the questions you might have in mind, this post will give an in-depth guide on Javascript high-order functions. However, first, have a refresher on the basic concepts of functional programming.

Functional Programming

Developers use many programming patterns, and functional programming is just one of them. Other patterns include object-oriented programming, procedural programming, etc.

One of the significant ideas behind Functional programming is Lambda Calculus – a comprehensive computing model. Together with Functional Programming, Lambda Calculus could be said to represent the following concepts:

  • The composition and use of functions can completely solve any computable task.
  • A function abstracts the complexity of an algorithm and takes a value, and returns another value. When working with pure functions, the output will always be the same when given the same input and has no side effects. Check out our post – “A Complete Guide to Pure and Impure Functions in JavaScript.
  • Functions are also values. They can be used as inputs for other functions and returned as values by other functions. You will learn more about this in the next section when looking at “functions as first-class citizens.” This is one of the features that enable Javascript to implement higher-order functions

Functions as First-Class Citizens

You might have probably heard a popular phrase by developers stating, “functions are first-class citizens in Javascript.” That’s because functions in Javascript can operate like any other data type, such as a string, object, or array.

Have a look at the interesting piece of code below. Here, the function favoriteSport() is operating as an object.

function favoriteSport(){
}

favoriteSport()

//Here, you add properties to the function
favoriteSport.outdoor = "Handball"
favoriteSport.indoor = "Monopoly"

console.log(favoriteSport.outdoor)
console.log(favoriteSport.indoor)

/*Output:
Handball
Monopoly

From the code above, you will notice that the properties outdoor and indoor are added to the function once it’s invoked. You can then access these properties as you would on an object.

Note: Although passing properties to a function is possible, it is not verified as a good programming practice.

As a summary of this section, below are some of the operations you can perform with functions as first-class citizens.

  • Assign functions to a variable
  • Use functions as input to another function or return a function as the “return value” of another function.
  • Store functions in arrays.
  • Set function as object properties.

When you look at all the above features concerning functions as first-class citizens, then look at the high-order function definition given above – you will notice these are the things that make it possible to implement high-order functions in Javascript.

With that in mind, you can dive into the main topic of today.

Higher-Order Functions

As stated in the definition, higher-order functions take functions as arguments or return functions as the “return value” or perform both operations.

Below are a few examples to give you a solid understanding of higher-order Javascript functions.

Functions That Take Other Functions as Arguments

Take a look at the code snippet below.

const numbers = [1, 2, 3, 4, 5, 6];

function parentFn(arr, fn) {
	for (let i = 0; i < numbers.length; i++) {
		fn(arr[i]);
	}
}

let doubledNums = [];

function childFn(x) {
	doubledNums.push(x * 2);
}

parentFn(numbers, childFn);

console.log("Initial Array: ",numbers)
console.log("Doubled Array: ",doubledNums);

/*Output:
Initial Array:  [ 1, 2, 3, 4, 5, 6 ]
Doubled Array:  [ 2, 4, 6, 8, 10, 12 ]

In the above example, parentFn() is the higher order function. It takes two arguments – an array (arr) and a function (fn). It loops through each element in the array and uses the fn() logic to manipulate these items. In this case, fn() is invoking the childfn() function which multiplies every array item by two.

Functions That Return Other Functions

Do you have a good understanding of JavaScript Currying? If not, check out this comprehensive post – A Complete Guide to JavaScript Currying: What Does it Mean? You will have a better understanding of what we will look at in this example.

Take a look at the code below.

function favoriteFruits(fruitOne) {
  return function (fruitTwo) {
    return function (fruitThree) {
      return "John Loves " + fruitOne +", " + fruitTwo +" and " + fruitThree;
    };
  };
}

console.log(favoriteFruits("mangoes")("bananas")("oranges"))
/*Output:
John Loves mangoes, bananas and oranges

Here, the favoriteFruits() function is a higher-order function that returns other functions which require other arguments. For example, it first returns a function that requires you to pass the fruitTwo argument; then, it returns a third function that requires you to pass a fruitThree argument. The last function will then return the actual value. In this case, it’s a string.

To invoke this function and pass arguments to the other functions, you will use the syntax favoriteFruits(argumentOne)(argumentOne)(argumentOne). Please, check that post on Javascript Currying to understand this concept in-depth.

Functions That Take Other Functions as Arguments and Return Other Functions

In the previous two sections, you have looked at functions that either take a function as an argument or return a function as the return value. Below is a function that performs both operations – it takes a function as an argument and returns a function.

const friends = ["John","Jane","Erick","June"]

function functionOne(arr, fn){
  for (let i = 0; i < friends.length; i++){
    fn(arr[i])
  }
  return function(school){
    console.log("We all went to "+school+" university")
  }
}

function functionTwo(x){
  console.log("My friend is: "+ x)
}
functionOne(friends,functionTwo)("Aghakan")

/*Output:
My friend is: John
My friend is: Jane
My friend is: Erick
My friend is: June
We all went to Aghakan university

In the above code, functionOne() is a higher-order function. It takes a function (functionTwo) as an argument and returns another function (function(school)). You will also notice a concept of currying incorporated in this particular example.

functionOne() has a “for loop” that iterates through the friends array and fetches each item. Each array item is passed to the fn() function, which invokes functionTwo() and prints the friend’s name.

After the “for loop” exits, functionOne will return a function (function(school)) which is involved when calling functionOne() as shown below.

functionOne(friends,functionTwo)("Aghakan")

Higher-Order Array Methods

Most of the built-in Javascript methods used to manipulate strings, DOM, and arrays are higher-order Javascript methods. This section will focus on the arrays. An array is a single variable that can store other elements, which include numbers, strings, objects, or other arrays.

Below are some of the array methods which are, in fact, higher-order functions.

Array.map()

The map function takes a callback function as an argument. It iterates through each element in the array and invokes the callback function on the element. The return value of the callback function is stored in a new array.

Tip: The Array.map() method does not mutate the array upon which it is called. Instead, it creates a new array with values returned by the callback function.

Assume you have an array of three digits, and you want to multiply each digit by two. How would you go about it?

Normal Way

Normally, this is how you would write your code.

const nums = [10, 20, 30];
let doubledNums = [];
function multiplyNums(arr) {
	for (let i = 0; i < arr.length; i++) {
		doubledNums.push(arr[i] * 2);
	}
}
multiplyNums(nums);
console.log(doubledNums);

/*Output:
[ 20, 40, 60 ]

Here, you are using a “for loop” to iterate through every single element in the array, multiplying the element by two and pushing it to the doubledNums array. Although you achieved what you wanted, there is a much simpler method.

Using Array.map()

Below is a code snippet you could use to achieve the same results using the map method.

const nums = [10,20,30]

let doubledNums = [];
nums.map((item)=> doubledNums.push(item*2));

console.log(doubledNums);

/*Output:
[ 20, 40, 60 ]

Here, you will have used less code, which is easy to read and debug.

Array.filter()

The array.filter() method is a higher-order function that takes a callback function as an argument. Like the map method, the array.filter iterates an array fetching each element and passing it to the callback function. If the element passes the condition in the callback function, it is stored in a new array.

Assume you had an array of student objects shown below, and you need to filter the students pursuing “Law.” How would you go about it?

const students =[
  {name:"John", course:"Medicine"},
  {name:"Jane", course:"Law"},
  {name:"Erick", course:"Law"},
  {name:"June", course:"Medicine"},
]

Normal Way

Normally, this is how you would write your code using the “for loop” statement.

let lawStudents=[]
function filterStudents(arr){
  for (let i = 0; i < students.length; i++){
    if(arr[i].course === "Law"){
      lawStudents.push(arr[i].name)
    }
  }
}
filterStudents(students)
console.log(lawStudents)

Here, you use a “for loop” to iterate the array and fetch elements. Each element is then passed to the “if statement,” and if it passes the condition, it pushes the name property to the lawStudents[] array

Using Array.filter()

With array.filter method, you will write less code that is easy to read and debug.

let lawStudents = students.filter(function(value){
  return value.course === "Law"
})

console.log(lawStudents)

/*Output:
[ { name: 'Jane', course: 'Law' },
{ name: 'Erick', course: 'Law' } ]

Here, the student.filter() method iterates through the students array and passes each element to the callback function. This function uses a condition to check which students pursue law and if it’s true, that student is added to a new array. This new array is then assigned to the lawStudents variable.

Array.reduce()

The array.reduce is an interesting array method and will always come in handy in most projects. If you are not well versed with this method, checkout the post – How to use Array Reduce in JavaScript: A Complete Guide.

As the name suggests, the array.reduce method reduces a function to a single value. When compared to the map() or the filter() methods, the reduce() method is a relatively complex higher-order function. This method accepts two arguments.

  • An initial value
  • A callback function

The callback function in turn also takes a couple of arguments.

  • Accumulator
  • Current value
  • Current index
  • Source Array

Assume you had an array of student’s marks as shown below.

const studentMarks =[40,45,37,25,55,65]

Normal Way

Normally, this is how you would probably write the code to add all the array elements.

let totalMarks = 0

for (let i =0; i < studentMarks.length; i++){
  totalMarks += studentMarks[i]
}

console.log(totalMarks)

/*Output:
267

Here, the “for loop” is iterating through the array and adding each element to the totalMarks variable.

Using Array.reduce()

This is how you would solve the same problem using the array.reduce() method.

const totalMarks = studentMarks.reduce(function(acc, value) {
  return acc + value;
});
console.log(totalMarks);

/*Output:
267

Advantages of Higher-Order Functions

  • They make code reusability much easier.
  • Higher-order functions are a great approach to separate and abstract program actions.
  • They enhance code simplicity. It’s much easier to read and debug code even for other developers.
  • Using higher-order functions save time in writing code, especially when working with complex applications.

Conclusion

That’s it! You now have an in-depth understanding of JavaScript Higher-Order Functions. From all the advantages listed above and those demonstrated in the example provided in this post, it’s time you start incorporating this concept into your projects. You can decide to use the higher-order built-in methods provided by Javascript or create your functions from scratch.

Was this article helpful? Do you have any suggestions or is there any additional information that you feel was left out?

You can also share your thoughts by replying on Twitter of Become A Better Programmer or to my personal account.