· By John Kavanagh

Higher‑Order Functions in JavaScript

Abstract image used to represent Higher‑Order Functions in JavaScript
Image by Cloe Thornton.

If you pass a callback to map() or filter(), or register an event listener with an API such as addEventListener(), you are already calling a higher‑order function. The listener is the callback; the API that accepts it is the higher‑order function. Passing behaviour as a value is useful when it makes the code easier to compose.


What are Higher‑Order Functions?

In JavaScript, a function is considered a higher‑order function if it does one or both of the following:

  • Accepts another function as an argument.
  • Returns a function as its result.

Here's a simple example of a higher‑order function:

const applyOperation = (operation: (a: number, b: number) => number, x: number, y: number): number => {
  return operation(x, y);
};

const add = (a: number, b: number) => a + b;
const multiply = (a: number, b: number) => a * b;

console.log(applyOperation(add, 5, 3));  // Output: 8
console.log(applyOperation(multiply, 5, 3));  // Output: 15

Here, applyOperation takes a function (operation) as an argument, allowing us to pass different functions to change its behaviour dynamically.


Why Use Higher‑Order Functions?

Higher‑order functions help us write cleaner and more maintainable code by promoting:

  • Code reusability

    – Common logic can be abstracted into reusable functions.
  • Modularity

    – Functions become more composable and easier to work with.
  • Functional programming principles

    – JavaScript's functional nature allows us to build more declarative and expressive code.

I'm very aware that this is a lot of words where a few examples might be easier to digest, so...


Built‑in Higher‑Order Functions in JavaScript

JavaScript provides several higher‑order functions that are commonly used when working with arrays and data transformations.

1. Array.prototype.map()

map() is a higher‑order function that creates a new array by applying a function to each element.

For example:

const numbers = [1, 2, 3, 4];
const doubled = numbers.map(num => num * 2);
console.log(doubled);  // Output: [2, 4, 6, 8]

2. Array.prototype.filter()

filter() creates a new array with elements that satisfy a given condition:

const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers);  // Output: [2, 4]

3. Array.prototype.reduce()

reduce() executes a reducer function on each element of an array, returning a single accumulated value, like this:

const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum);  // Output: 10

Returning Functions from Functions

Higher‑order functions can also return functions, allowing for greater flexibility and composition. For example:

const createMultiplier = (factor: number) => (num: number) => num * factor;

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5));  // Output: 10
console.log(triple(5));  // Output: 15

In this example, createMultiplier returns a new function that multiplies a given number by the provided factor. This approach is useful for function factories and custom utilities.


Practical Uses of Higher‑Order Functions

1. Function Composition

Function composition is the process of combining multiple functions into a single function, like this:

const greet = (name: string) => `Hello, ${name}!`;
const excited = (str: string) => str.toUpperCase();

const greetExcited = (name: string) => excited(greet(name));
console.log(greetExcited("Maddie"));  // Output: HELLO, MADDIE!

2. Event Listeners

Higher‑order functions are commonly used in event handling, too.

const handleClick = (callback: () => void) => {
  document.addEventListener("click", callback);
};

handleClick(() => console.log("Button clicked!"));

In this way, we can abstract away event handling, making it reusable across different elements.


Wrapping Up

Key Takeaways

  • Higher‑order functions take functions as arguments or return functions.
  • JavaScript provides built‑in higher‑order functions like map(), filter(), and reduce().
  • Returning functions from functions enables function composition and reusable utilities.
  • Higher‑order functions improve modularity, reusability, and readability in JavaScript applications.

Higher‑order functions fit the examples here because callers supply behaviour to collection methods and event APIs, while factory functions return configured behaviour for later use. A small named callback is often clearer than a dense inline one. The abstraction has done its job when it removes repetition or exposes a decision without hiding the underlying flow.


Have a complex web platform issue?

Tell me what is blocked, what has changed, and what needs to be true after the fix. I'll come back with a practical next step.