Higher Order functions in JavaScript -
A brief introduction

Higher Order functions in JavaScript - A brief introduction

What are Higher-Order functions?

In JavaScript, functions are treated as first-class citizens, which means behind the scenes they are just another object. This feature enables the use of functions just like any other variable.

A higher-order function is just a function that can take another function as an argument or can return a function.

Advantages of using higher-order functions:

  1. Abstraction: Higher-order functions allow you to abstract common functionality into reusable components. This can reduce duplicated code and make your code more readable.

  2. Flexibility: Higher-order functions can be passed around as data. This allows you to create functions that can be adapted to various scenarios.

  3. Reuse: Higher-order functions can be reused throughout your code. This makes it easier to manage your code.

How to use higher-order functions:

Let's now look at how we can use the higher-order function and our code.

Remember, higher-order functions can return a function or take in a function as an argument because functions behave like any other object in JavaScript and so can be treated as a data value.

1️⃣ A function that returns a function.

In the below example, we make a function adder which takes num as input and returns a function that will add num to the new function's input.

Here, makeAdder is a higher-order function.

// makeAdder is the higher-order function that returns a function
function makeAdder(num) {
  return function(x) {
    return x + num;
  }
}

let add10 = makeAdder(10); // returns a function to add 10 to the input
let add5 = makeAdder(5); // returns a function to add 5 to the input

console.log(add10(3)); // logs 13 (10 + 3)
console.log(add5(add5(add5(10)))); // logs 25 (10 + 5 + 5 + 5)

Now of course the addition of two numbers can be done more simply. But here we are trying to understand how a higher-order function works.

Here, add10 and add5 are now independent functions themselves which we can call any number of times to add 10 and 5 to any number, respectively.

2️⃣ A function that takes a function as an argument.

Let's first understand the need to pass a function inside a function as an argument.

Take a look at the code below.

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

function doubleArray(arr) {
  let newArr = [];
  for (let i = 0; i < arr.length; i++) {
    newArr.push(arr[i] * 2);
  }
  return newArr;
}

function tripleArray(arr) {
  let newArr = [];
  for (let i = 0; i < arr.length; i++) {
    newArr.push(arr[i] * 3);
  }
  return newArr;
}

console.log(doubleArray(numbers)); // [2, 4, 6, 8, 10]
console.log(tripleArray(numbers)); // [3, 6, 9, 12, 15]

We needed a function that can update an array of integers passed in, with some arithmetic operations done on each element. An operation as simple as multiplying by 2 and 3, needed us to make two whole new functions.

Here you can see both the functions and very well figure out that a lot of the code has been repeated, which is against the DRY (Don't Repeat Yourself) principle.

So now, what to do? Now is when a higher-order function comes to our aid.

In both the functions above, the only difference is in the arithmetic operation performed on each element of the input array. So we need a way to delegate the behavior somewhere else. You guessed it right! Let's delegate it to a function.

//updateArray is higher-order function that takes function as an argument
function updateArray(arr, func) {
  let newArr = [];
  for (let i = 0; i < arr.length; i++) {
    newArr.push(func(arr[i]));
  }
  return newArr;
}

function double(num) {
  return num * 2;
}

function triple(num) {
  return num * 3;
}
const numbers = [1, 2, 3, 4, 5];

//Here we call our higher order function with a function that we need
const doubledArray = updateArray(numbers, double);
const tripledArray = updateArray(numbers, triple)

console.log(doubledArray); // [2, 4, 6, 8, 10]
console.log(tripledArray); // [3, 6, 9, 12, 15]

Here, we delegated the behavior that we wanted to double & triple and passed them function inside our main updateArray function as an argument.

You can see how this helped us to avoid repeated code. The use case of passing functions as arguments are many more. This was just a simple demo case.

3️⃣ Callback Functions:

Higher-order functions are commonly used with array methods such as map, filter & reduce. Here the functions passed inside the methods are also called callback functions.

Let's see an example of a callback function being used inside an Array method.

let arr = [1,2,3,4,5];
let fiveTimes = function(element) {
    return element * 5;
}
let newArr = arr.map(fiveTimes);
console.log(newArr); // [5, 10, 15, 20, 25]

Here, map is a higher-order function that is used to map over all the elements of an Array. It expects a callback function as an argument.

Hence we can see the power of callback functions being passed into high-order order functions.

Conclusion:

Higher-order functions are a powerful feature of JavaScript that can improve your code's readability, flexibility, and maintainability.

By abstracting and manipulating functions as first-class citizens, you can write more modular and reusable code.

I hope this blog has provided a good introduction to higher-order functions in JavaScript. Happy coding!