Write JavaScript like a pro. Javascript Icon

Follow the ultimate JavaScript roadmap.

Higher Order Functions in JavaScript

Higher order functions is a term given to functions that operate with other functions.

They are functions that accept a function via an argument, or functions that return a function.

You likely use them all the time, perhaps without realizing their conceptual name.

The benefit to using higher order functions is concise, minimal and functional-style code.

They allow us to abstract away repeatable logic and hide it inside easily-testable functions.

Let’s begin with a simple data structure:

const cart = [
  { name: 'Lemonade', price: 299 },
  { name: 'Coffee', price: 399 },
  { name: 'Water', price: 199 }
];

From here, we’re going to demonstrate both kinds of higher order functions.

The first, a function that accepts another function as an argument.

Easy. Let’s use Array.prototype.reduce:

const cart = [
  { name: 'Lemonade', price: 299 },
  { name: 'Coffee', price: 399 },
  { name: 'Water', price: 199 }
];

const total = cart.reduce((prev, next) => prev + next.price, 0);

console.log(total); // 897

Functions are just like any other data type in JavaScript and can be passed around just like other variables.

The other characteristic of a higher order function is “a function that returns a function”. This is a little more advanced and probably the one you’re most interested in using, seeing as passing a function as an argument is fairly trivial.

In functional programming, this can be commonly used in a composition style to create abstraction layers that are simple and reliable.

First, let’s create a quick add to cart function:

function addToCart(drink) {
  cart.push(drink);
}

This is great, it’s concise, simple, testable. But how could we use it as part of an abstraction elsewhere?

Discounts! A fairly common use case…

If we wanted to give a 40% discount, we could multiple by 0.6 on the way into the addToCart call:

function addToCart(drink) {
  cart.push(drink);
}

const drink = { name: 'Lemonade', price: 299 };
const price = drink.price * 0.6; // apply discount
addToCart({ ...drink, price }); // set the new price, add to cart

This approach works, but leaves us open to making simple mistakes, and also repeating lots of logic and not really following any pattern or structure.

Enter the higher order function, that returns us a function.

function addToCart(drink) {
  cart.push(drink);
}

function addToCartWithDiscount(amount) {
  return function(drink) {
    const price = drink.price * amount;
    addToCart({... drink, price });
  };
}

What we’re doing here is intercepting the price property on its way into the addToCart function, and multiplying it by a discount amount.

This makes it dynamic, leaving it open for composition.

Let’s setup a reusable function that always applies a 40% discount.

When we call that function it returns a new function, that we assign to a variable to use later:

function addToCartWithDiscount(amount) {
  return function(drink) {
    const price = drink.price * amount;
    addToCart({... drink, price });
  };
}

// call our function and apply 40% discount
const addToCartWith40Discount = addToCartWithDiscount(0.6);
// object to pass to cart
const drink = { name: 'Lemonade', price: 299 };
// automatically apply 40% discount with a function call
addToCartWith40Discount(drink);

Note how there are no random variables or error-prone calculations floating around.

Pay attention here, it’s where things get juicy.

Now we can setup more higher order functions that perform a single action for us:

function addToCartWithDiscount(amount) {
  return function(drink) {
    const price = drink.price * amount;
    addToCart({... drink, price });
  };
}

const addToCartWith20Discount = addToCartWithDiscount(0.8); // 💰 20%
const addToCartWith40Discount = addToCartWithDiscount(0.6); // 💰 40%
const addToCartWith60Discount = addToCartWithDiscount(0.4); // 💰 60%
const addToCartWith80Discount = addToCartWithDiscount(0.2); // 💰 80%

And use them however, and wherever, we like:

const drink = { name: 'Lemonade', price: 299 };

addToCartWith40Discount(drink); // 💰 40% off

console.log(cart); // ✅ { name: 'Lemonade', price: 179.4 }

Small reliable functions, easy to test, clear and concise, a win-win.

This concept goes hand-in-hand with understanding closures, which give us ultimate power and functional composition.

Learn JavaScript the right way.

The most complete guide to learning JavaScript ever built.
Trusted by 82,951 students.

Todd Motto

with Todd Motto

Google Developer Expert icon Google Developer Expert

Related blogs 🚀

Free eBooks:

Angular Directives In-Depth eBook Cover

JavaScript Array Methods eBook Cover

NestJS Build a RESTful CRUD API eBook Cover