DEV Community

Cover image for Do you know how it works? - Higher Order Functions (HOFs)
Matheus Julidori
Matheus Julidori

Posted on • Edited on

Do you know how it works? - Higher Order Functions (HOFs)

TL;DR:

  • Higher-Order Functions (HOFs) are functions that receive or return other functions.
  • They’re key to functional programming, widely used in React, map(), filter(), and more.
  • They help abstract repetitive logic, making your code cleaner, testable, and elegant.
  • Classic examples: setTimeout, Array.map(), Array.filter(), function factories.

Introduction

Higher-Order Functions (HOFs), or First-Class Functions, became a hot topic in JavaScript over the last few years — largely thanks to functional programming principles and React.

But despite the intimidating name, HOFs are actually pretty simple. They can be defined as:

  • Functions that takes one (or more) functions as parameters, or
  • Functions that return another function

But what’s the point of passing a function as a parameter? Going deeper: why even pass parameters to a function at all? What’s the use?


Parameters in Functions

Function parameters play a crucial role. Functions that receive parameters usually either perform some action with that value or transform it.

For example, take a binary search function, which receives an array of elements and a target value to search for:

function binarySearch(array, value) {
    let left = 0;
    let right = array.length - 1;

    while (left <= right) {
        const mid = left + Math.floor((right - left) / 2);

        if (array[mid] === value) {
            return mid;
        } else if (array[mid] < value) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }

    return -1;
}

const numbers = [1, 3, 5, 7, 9, 11, 13, 15];
console.log(binarySearch(numbers, 7));  // 3
console.log(binarySearch(numbers, 10)); // -1
Enter fullscreen mode Exit fullscreen mode

The function needs the target value to know what to look for, and the array to know where to look and what index to return.

But sometimes, passing primitive values like numbers, arrays, or strings isn’t enough. In more complex cases, the logic itself needs to be passed as a function.


Functions as Parameters

A perfect example of a function that needs another function is setTimeout(). This function executes an action after a delay. But it needs to know what to execute — which is why we pass it a function.

Think of it like calling your ISP and following the steps to reset your modem:

  • Turn it off
  • Wait 5 seconds
  • Turn it back on
const modem = {
    on: true,
    toggle() {
        this.on = !this.on;
        console.log(this.on);
    },
};

// Turn off the modem
modem.toggle();
// Turn it back on after 5000ms (5s)
setTimeout(modem.toggle, 5000);
Enter fullscreen mode Exit fullscreen mode

Passing a number alone wouldn’t be enough for setTimeout() — it needs a function to execute after the time delay.

Other common examples include Array.map() and Array.filter().

If you, like me, learned programming in a language with no functional programming (or sometimes not even OOP — shoutout to the C devs, I do not miss that), you probably ignored these functions and wrote loops instead:

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let lessThan4 = [];
let j = 0;

for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] < 4) {
        lessThan4[j] = numbers[i];
        j++;
    }
}
Enter fullscreen mode Exit fullscreen mode

But when you need to chain multiple steps, this quickly becomes messy and unreadable. Suppose we want to filter even numbers less than 4, then multiply them by 2:

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let lessThan4 = [];
let j = 0;

// Filter < 4
for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] < 4) {
        lessThan4[j] = numbers[i];
        j++;
    }
}

// Filter evens
let evens = [];
j = 0;

for (let i = 0; i < lessThan4.length; i++) {
    if (lessThan4[i] % 2 === 0) {
        evens[j] = lessThan4[i];
        j++;
    }
}

// Multiply by 2
let result = [];

for (let i = 0; i < evens.length; i++) {
    result[i] = evens[i] * 2;
}

console.log(result); // [4]
Enter fullscreen mode Exit fullscreen mode

That’s a lot of logic for a simple transformation. With HOFs, we abstract all that into readable, functional chains using filter() and map():

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

let result = numbers
    .filter(n => n < 4)
    .filter(n => n % 2 === 0)
    .map(n => n * 2);

console.log(result); // [4]
Enter fullscreen mode Exit fullscreen mode

And if the logic gets more complex or will be reused, you can define it in named functions and pass those:

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

let result = numbers
    .filter(lessThan4)
    .filter(isEven)
    .map(double);

function lessThan4(n) {
    return n < 4;
}

function isEven(n) {
    return n % 2 === 0;
}

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

console.log(result); // [4]
Enter fullscreen mode Exit fullscreen mode

This makes your code more readable, reusable, and testable. (Seriously — write tests. Your future self will thank you.)


Returning Functions

As I said earlier, functions that return other functions are also considered HOFs.

Let’s say you have multiple buttons, and each applies a different theme to the page (light, dark, high-contrast). But besides applying styles, you also want to:

  • Save the preference to localStorage
  • Log which theme was selected
<button onclick="applyLightTheme()">Light</button>
<button onclick="applyDarkTheme()">Dark</button>
<button onclick="applyHighContrastTheme()">High Contrast</button>

<script>
function applyLightTheme() {
    document.body.className = 'light';
    localStorage.setItem('theme', 'light');
    console.log('Theme: light');
}

function applyDarkTheme() {
    document.body.className = 'dark';
    localStorage.setItem('theme', 'dark');
    console.log('Theme: dark');
}

function applyHighContrastTheme() {
    document.body.className = 'contrast';
    localStorage.setItem('theme', 'contrast');
    console.log('Theme: contrast');
}
</script>
Enter fullscreen mode Exit fullscreen mode

But the logic is repeating — and we don’t like repetition. So we use a Function Factory (not to be confused with Factory Functions 😄).

A function factory is a HOF — it builds and returns a function:

<button onclick="applyLightTheme()">Light</button>
<button onclick="applyDarkTheme()">Dark</button>
<button onclick="applyHighContrastTheme()">High Contrast</button>

<script>
function makeThemeApplier(themeName) {
    return function () {
        document.body.className = themeName;
        localStorage.setItem('theme', themeName);
        console.log(`Theme: ${themeName}`);
    };
}

const applyLightTheme = makeThemeApplier('light');
const applyDarkTheme = makeThemeApplier('dark');
const applyHighContrastTheme = makeThemeApplier('contrast');
</script>
Enter fullscreen mode Exit fullscreen mode

The makeThemeApplier() function is a Higher-Order Function because it receives a value and returns a function that remembers that value (thanks to closures).

That’s the core power of HOFs:

“Give me a parameter, and I’ll give you back a function that uses it.”

And if you’re wondering what a closure is — don’t worry. That’s exactly what we’ll explore in the next episodes.

Recap

A Higher-Order Function:

  • Accepts a function as an argument, or
  • Returns a new function

They're everywhere: setTimeout(), map(), filter(), reduce()
They help you:

  • Abstract logic
  • Write cleaner code
  • Compose behavior

Enable powerful patterns like:

  • Function factories
  • Closures
  • Declarative pipelines

Perfeito! Aqui está seu post formatado para publicação no dev.to, com título, seções, marcação em markdown e preservando o estilo da série “Do You Know How It Works?”:


Wrapping Up

HOFs are a gateway to functional programming in JavaScript.
They help you write expressive, declarative, and clean code.

If you’ve ever wondered why map() and filter() feel so magical — it’s because they are.

But we’ve only scratched the surface.

Next up: JavaScript Scopes
What exactly can your function “see”?
Let’s uncover the rules behind variable visibility, hoisting, and why sometimes things just don’t exist where you expect them to..


💬 Have a cool use case for HOFs?
Drop it in the comments — I’d love to learn how you’re using them in your projects!

📬 If you're enjoying this series, consider saving it or sharing it with a fellow JS dev. Let’s grow together!

👉 Follow me @matheusjulidori for the next episodes of Do You Know How It Works?

🎥 Subscribe to my YouTube channel to be the first ones to watch my new project as soon as it is released!

Top comments (0)