DEV Community

Cover image for Higher-Order Functions(HoF) in JavaScript - Explain Like I'm Five
Tapas Adhikary
Tapas Adhikary

Posted on • Originally published at blog.greenroots.info

Higher-Order Functions(HoF) in JavaScript - Explain Like I'm Five

JavaScript Functions

Functions are an integral part of many programming languages, and JavaScript is not an exception. In JavaScript, functions are the first-class citizens. You create them, assign them as a value, pass them as arguments to other functions, also return them as a value from a function.

These flexibilities help in code reusability, clean code, and composability. Today we will learn about Higher-Order Functions to use functions to their full potential in JavaScript.

If you like to learn from video content as well, this article is also available as a video tutorial here: 🙂

Don't forget to subscribe for the future content.

What are Higher-Order Functions?

A Higher-Order Function is a regular function that takes one or more functions as arguments and/or returns a function as a value from it.

Here is an example of a function that takes a function as an argument.

// Define a function that takes a function as an argument.
function getCapture(camera) {
  // Invoke the passed function
  camera();
}

// Invoke the function by passing a function as an argument
getCapture(function(){
  console.log('Canon');
});
Enter fullscreen mode Exit fullscreen mode

Now let us take another function that returns a function.

// Define a function that returns a function
function returnFunc() {
  return function() {
    console.log('Hi');
  }
}

// Take the returned function in a variable.
const fn = returnFunc();
// Now invoke the returned function.
fn(); // logs 'Hi' in the console

// Alternatively - A bit odd syntax but good to know
returnFunc()(); // logs 'Hi' in the console
Enter fullscreen mode Exit fullscreen mode

Both of the examples above are examples of Higher-Order functions. The functions getCapture() and returnFunc() are Higher-Order functions. They either accept a function as an argument or return a function.

Please note, it is not mandatory for a Higher-Order function to perform both accepting an argument and returning a function. Performing either will make the function a Higher-Order function.

Why use Higher-Order Functions? How to create Higher-Order Functions?

So, we understand what a Higher-Order function is. Now, let us understand why we need one and how to create it? How about doing it with a few simple examples.

The Problem: Code Pollution and Smell

Let's take an array of numbers,

const data = [12, 3, 50];
Enter fullscreen mode Exit fullscreen mode

Now let's write code to increment each array element by a number and return the modified array. You may think about writing it as a function.

function incrArr(arr, n) {
  let result = [];

  // Iterate through each elements and
  // add the number
  for (const elem of arr) {
    result.push(elem + n);
  }

  return result;
}
Enter fullscreen mode Exit fullscreen mode

So, if we do,

incrArr(data, 2);
Enter fullscreen mode Exit fullscreen mode

Output,

[14, 5, 52]
Enter fullscreen mode Exit fullscreen mode

Great so far. Now, if I ask you to write code to decrement each of the elements of the data array by a number and return the modified array? You may think about solving it in a couple of straightforward ways. First, you can always write a function like,

function decrArr(arr, n) {
  let result = [];

  for (const elem of arr) {
    result.push(elem - n);
  }

  return result;
}
Enter fullscreen mode Exit fullscreen mode

But that's lots of code duplication. We have written almost every line of the incrArr() function in the decrArr() function. So, let's think about the reusability here.

Now, you may want to optimize the code to have one single function performing both these operations conditionally.

function doOperations(arr, n, op) {
  let result = [];

  for (const elem of arr) {
    if (op === 'incr') {
      result.push(elem + n);  
    } else if (op === 'decr') {
      result.push(elem - n);
    }
  }

  return result;
}
Enter fullscreen mode Exit fullscreen mode

So, now we rely on a third argument to decide if the operation is to increment or decrease the array's number. There is a problem too. What if I ask you to multiply each element of an array by a number now? You may think about adding another else-if in the doOperations() function. But that's not cool.

For every new operation, you need to change the logic of the core function. It makes your function polluted and will increase the chance of code smells. Let's use the Higher-Order function to solve this problem.

The Solution: Higher-Order Function

The first thing to do is create pure functions for the increment and decrement operations. These functions are supposed to do only one job at a time.

// Increment the number by another number
function incr(num, pad) {
  return num + pad;
}

// Decrement the number by another number
function decr(num, pad) {
  return num - pad;
}
Enter fullscreen mode Exit fullscreen mode

Next, we will write the Higher-Order function that accepts a function as an argument. In this case, the passed function will be one of the pure functions defined above.

function smartOperation(data, operation, pad) {
  // Check is the passed value(pad) is not a number.
  // If so, handle it by assigning to the zero value.
  pad = isNaN(pad) ? 0 : pad;

  let result = [];
  for (const elem of data) {
    result.push(operation(elem, pad));
  }
  return result;
}
Enter fullscreen mode Exit fullscreen mode

Please observe the above function closely. The first parameter is the array to work on. The second parameter is the operation itself. Here we pass the function directly. The last parameter is the number that you want to increament or decerement.

Now, let's invoke the function to increment array elements by three.

const data = [12, 3, 50];
const result = smartOperation(data, incr, 3);
console.log(result);
Enter fullscreen mode Exit fullscreen mode

Output,

[15, 6, 53]
Enter fullscreen mode Exit fullscreen mode

How about trying the decrement operation now?

const data = [12, 3, 50];
const result = smartOperation(data, decr, 2);
console.log(result);
Enter fullscreen mode Exit fullscreen mode

Output,

[10, 1, 48]
Enter fullscreen mode Exit fullscreen mode

Did you notice that we didn't make any changes to our function to accommodate a new operation this time? That's the beauty of using the Higher-Order function. Your code is smell-free and pollution-free. So, how do we accommodate a multiplication operation now? Easy, let's see.

First, create a function to perform multiplication.

function mul(num, pad) {
  return num * pad;
}
Enter fullscreen mode Exit fullscreen mode

Next, invoke the Higher-Order function by passing the multiplication operation function, mul().

const data = [12, 3, 50];
const result = smartOperation(data, mul, 3);
console.log(result);
Enter fullscreen mode Exit fullscreen mode

Output,

[36, 9, 150]
Enter fullscreen mode Exit fullscreen mode

That's incredible. Long live Higher-Order functions.

In-built Higher-Order Functions in JavaScript

In JavaScript, there are plenty of usages of higher-order functions. You may be using them without knowing them as Higher-Order functions.

For example, take the popular Array methods like, map(), filter(), reduce(), find(), and many more. All these functions take another function as an argument to apply it to the elements of an array.

Here is an example of the filter() method that filters the array elements based on the condition we pass to it as part of the function argument.

const data = [1, 23, 45, 67, 8, 90, 43];

const result = data.filter(function(num){
    return (num % 2 === 0);
});

console.log(result); // [8, 90]
Enter fullscreen mode Exit fullscreen mode

Higher-Order Functions vs Callback functions

There is always some confusion between the Higher-Order functions and callback functions. Higher-Order Functions(HoF) and Callback Functions(CB) are different.

  • Higher-Order Functions(HoF): A function that takes another function(s) as an argument(s) and/or returns a function as a value.
  • Callback Functions(CB): A function that is passed to another function.

Conclusion

To conclude, the Higher-Order function is a fundamental concept built in the JavaScript language. We need to find opportunities to leverage it as much as possible in our coding practices. Higher-Order function in conjunction with the pure function will help you keep your code clean and side effects free.

I will leave you with this article on Pure Function and Side Effects in JavaScript. I hope you enjoy reading it as well.

What are pure functions and side effects in javascript

You can find all the source code used in the article in this stackblitz project.

https://stackblitz.com/edit/learn-js-hof



I hope you found this article insightful. Thanks for reading. Please like/share so that it reaches others as well.

Let's connect. I share my learnings on JavaScript, Web Development, Career, and Content on these platforms as well,

Top comments (12)

Collapse
 
lokmanhossainpappu profile image
LokmanHossainPappu

great

Collapse
 
atapas profile image
Tapas Adhikary

Thank you!

Collapse
 
eljayadobe profile image
Eljay-Adobe

Excellent! And you avoided mentioning monad and scaring away the fainthearted.

Collapse
 
suhakim profile image
sadiul hakim

Good job

Collapse
 
atapas profile image
Tapas Adhikary

Thanks a lot 😀

Collapse
 
tleperou profile image
Thomas Lepérou

I still struggle with HoF posts which don't mention closure. I either misunderstand closure concept or HoF purpose. Any guidance?

Collapse
 
atapas profile image
Tapas Adhikary

Hey Thomas, thanks for reading. I kept out closure intentionally in this post. Closure and HoF are integral to each other but when I started explaining it I wanted to focus on the simlicity of understanding HoF with simple examples. The next in the line is to go closure and currying to complete the triology 😀.

Collapse
 
bogdanbatsenko profile image
bogdanbatsenko

Well done. However, I would like to know more about the difference between callback and HOF

Collapse
 
atapas profile image
Tapas Adhikary

Thank you! I have done a detailed explanation of the callback here: freecodecamp.org/news/javascript-c...

Collapse
 
ijash profile image
Jastria Rahmat

So, is HOF is the same concept as Decorator as in python?

And if it's OOP, can this be called some kind of inheritance?

Collapse
 
atapas profile image
Tapas Adhikary

I'm not aware much on Python so can not confirm about it but coming to the OOPs part, it is not exactly inheritance. Inheritance is where we get pass the parent traits to child and also enhance them.

HoF is more to do with function compositions. You create function compose to another, the the output can be composed to another one, and so on.

Collapse
 
tleperou profile image
Thomas Lepérou

JS has landed not that long ago decorators for OOP (mostly) like Python does.