# JavaScript Higher-Order Functions: Functions That Use Functions

Source: https://devdocs10.com/articles/javascript/js-higher-order  
By Murat Bo · Published 2026-07-08 · DevDocs10 (https://devdocs10.com)

Higher-order functions are one of the most useful ideas in JavaScript because they let you build flexible, reusable code by passing functions around like values. They power common array methods such as `map`, `filter`, and `reduce`, and they help you write code that is easier to compose and maintain.

> **Quick answer:** A higher-order function is any function that either takes another function as an argument, returns a function, or both. In JavaScript, they are a core part of how callbacks, array methods, and functional-style code work.

> **Difficulty:** Beginner
>
> **Helpful to know first:** You’ll understand this better if you know how to write basic functions, pass values into functions, and use arrays in JavaScript.

## 1. What Is Higher-Order Functions?

A higher-order function is a function that works with other functions as inputs or outputs. Instead of only accepting strings, numbers, or objects, it can accept a function, return a function, or do both.

- It treats functions like regular values.
- It can receive a function to customize behavior.
- It can return a function to create specialized behavior.
- It is a common pattern in modern JavaScript APIs.

For example, `Array.prototype.map()` is a higher-order function because you pass it a function that tells JavaScript how to transform each element.

## 2. Why Higher-Order Functions Matter

Higher-order functions matter because they reduce repetition and make code more expressive. Instead of writing loops for every small data transformation, you can describe what should happen and let a function handle the iteration.

They are especially useful when you want to:

- transform arrays
- filter data based on a condition
- reuse behavior with different inputs
- wrap logic such as logging, timing, validation, or retries

They also make it easier to separate *what* you want to do from *how* it is done.

## 3. Basic Syntax

The core idea is simple: a function can accept another function as a parameter, or it can return a function.

### Receiving a function as an argument

In this example, `greet` is a higher-order function because it accepts `formatName`, which is itself a function.

```javascript
function greet(formatName) {
  const name = "Ada Lovelace";
  return formatName(name);
}

function toUpperCaseName(value) {
  return value.toUpperCase();
}

console.log(greet(toUpperCaseName)); // ADA LOVELACE
```

Output (tested with Node 24.21.0):

```text
ADA LOVELACE
```

Here, `greet` does not care how the name is formatted. It only knows that it can call the function it receives.

### Returning a function

A function can also create and return another function. This is common when you want a reusable function with built-in configuration.

```javascript
function createMultiplier(factor) {
  return function (number) {
    return number * factor;
  };
}

const double = createMultiplier(2);
console.log(double(5)); // 10
```

Output (tested with Node 24.21.0):

```text
10
```

This works because the returned function remembers the `factor` through a closure.

## 4. Step-by-Step Examples

### Example 1: Using `map` to transform data

`map` is a classic higher-order function. It takes a function and uses it to transform every item in an array.

```javascript
const prices = [10, 20, 30];

const withTax = prices.map(function (price) {
  return price * 1.2;
});

console.log(withTax); // [12, 24, 36]
```

Output (tested with Node 24.21.0):

```text
[ 12, 24, 36 ]
```

The callback tells `map` how to transform each number. You get a new array back without modifying the original one.

### Example 2: Using `filter` to keep selected items

`filter` is higher-order because it receives a function that returns `true` or `false`.

```javascript
const users = [
  { name: "Mia", active: true },
  { name: "Noah", active: false },
  { name: "Lina", active: true }
];

const activeUsers = users.filter(function (user) {
  return user.active;
});

console.log(activeUsers);
```

Output (tested with Node 24.21.0):

```text
[ { name: "Mia", active: true }, { name: "Lina", active: true } ]
```

The function passed to `filter` decides which items stay in the result.

### Example 3: Using `reduce` to combine values

`reduce` is a higher-order function that takes a callback used to accumulate a single result from many values.

```javascript
const numbers = [1, 2, 3, 4];

const sum = numbers.reduce(function (total, current) {
  return total + current;
}, 0);

console.log(sum); // 10
```

Output (tested with Node 24.21.0):

```text
10
```

The callback runs once for each item and gradually builds the final value.

### Example 4: Returning a specialized function

You can use a higher-order function to configure behavior once and reuse it many times.

```javascript
function createLogger(prefix) {
  return function (message) {
    console.log(prefix + ": " + message);
  };
}

const logInfo = createLogger("INFO");
logInfo("Server started");
```

Output (tested with Node 24.21.0):

```text
INFO: Server started
```

This pattern is useful when you want to reuse the same behavior with different settings.

## 5. Practical Use Cases

Higher-order functions appear all over real JavaScript code. They are not just a functional-programming idea; they are a practical tool for everyday development.

- Processing arrays of data such as products, users, or search results
- Creating reusable validators like email or password checks
- Adding logging, timing, or analytics around functions
- Building custom sort rules with `Array.prototype.sort()`
- Handling async callbacks such as timers, event listeners, and promise chains

Whenever you need to plug custom logic into a reusable mechanism, a higher-order function is often the right fit.

## 6. Common Mistakes

### Mistake 1: Passing a function call instead of a function

When a higher-order function expects a function, beginners sometimes call the function immediately and pass its return value instead.

**Problem:** The callback is executed too early, so the outer function receives a value like a string or number instead of a function. That often leads to `TypeError: callback is not a function`.

```javascript
function sayHello(name) {
  return "Hello, " + name;
}

function runLater(callback) {
  return callback("Ada");
}

runLater(sayHello("Ada"));
```

**Fix:** Pass the function reference, not the result of calling it.

```javascript
function sayHello(name) {
  return "Hello, " + name;
}

function runLater(callback) {
  return callback("Ada");
}

runLater(sayHello);
```

The corrected version works because `runLater` receives a callable function.

### Mistake 2: Forgetting to return from the callback

Many array methods, including `map` and `filter`, depend on the callback returning a value.

**Problem:** If the callback uses braces but does not include `return`, it returns `undefined`. In `map`, that produces an array of `undefined` values.

```javascript
const values = [1, 2, 3];

const doubled = values.map(function (value) {
  value * 2;
});

console.log(doubled); // [undefined, undefined, undefined]
```

Output (tested with Node 24.21.0):

```text
[ undefined, undefined, undefined ]
```

**Fix:** Return the computed value from the callback.

```javascript
const values = [1, 2, 3];

const doubled = values.map(function (value) {
  return value * 2;
});

console.log(doubled); // [2, 4, 6]
```

Output (tested with Node 24.21.0):

```text
[ 2, 4, 6 ]
```

The fixed version works because each callback invocation contributes a real result.

### Mistake 3: Trying to pass a method without preserving its object

Some methods depend on `this`. If you pass the method around by itself, it may lose the object it came from.

**Problem:** The method is detached from its object, so `this` is not what the method expects. That can produce confusing runtime behavior or errors such as `Cannot read properties of undefined`.

```javascript
const calculator = {
  factor: 3,
  multiply(n) {
    return n * this.factor;
  }
};

const items = [1, 2, 3];
const results = items.map(calculator.multiply);

console.log(results);
```

Output (tested with Node 24.21.0):

```text
[ NaN, NaN, NaN ]
```

**Fix:** Bind the method or wrap it in an arrow function so the object stays available.

```javascript
const calculator = {
  factor: 3,
  multiply(n) {
    return n * this.factor;
  }
};

const items = [1, 2, 3];
const results = items.map(value => calculator.multiply(value));

console.log(results); // [3, 6, 9]
```

Output (tested with Node 24.21.0):

```text
[ 3, 6, 9 ]
```

The corrected version works because the method is called with the original object.

## 7. Best Practices

### Practice 1: Prefer named functions when the callback is reused

Anonymous callbacks are fine for short one-off tasks, but named functions make repeated behavior easier to test and understand.

```javascript
function isAdult(person) {
  return person.age >= 18;
}

const adults = people.filter(isAdult);
```

A named callback gives your code a clear purpose and makes debugging easier.

### Practice 2: Keep callbacks small and focused

Callbacks become hard to read when they do too many jobs at once. Small callbacks are easier to reuse and reason about.

```javascript
const labels = products.map(function (product) {
  return product.name + " ($" + product.price + ")";
});
```

A concise callback keeps the higher-order function easy to scan.

### Practice 3: Use higher-order functions to separate configuration from behavior

If something changes often, put the changing part in the callback or returned function instead of hardcoding it.

```javascript
function createComparator(direction) {
  return function (a, b) {
    return direction === "asc" ? a - b : b - a;
  };
}

const numbers = [5, 1, 9];
console.log(numbers.sort(createComparator("asc")));
```

Output (tested with Node 24.21.0):

```text
[ 1, 5, 9 ]
```

This keeps the sorting logic reusable and avoids duplication.

## 8. Limitations and Edge Cases

- Not every function is higher-order. A function that only works with local values and never accepts or returns functions is not higher-order.
- Callbacks may be called many times, so expensive work inside them can slow down array operations.
- Some higher-order functions, like `sort`, mutate the original array, while others like `map` and `filter` create new arrays.
- Arrow functions and regular functions handle `this` differently, which matters when you pass methods as callbacks.
- Async callbacks can be confusing: passing an `async` function to a higher-order function does not always wait for the result unless the API is designed for promises.

One common surprise is that `forEach` does not return a transformed array. It is higher-order, but it is meant for side effects, not data transformation.

## 9. Practical Mini Project

Let’s build a small task-processing utility that uses higher-order functions to create flexible filters and transformations. This shows how higher-order functions help you avoid repeating nearly identical logic.

```javascript
const tasks = [
  { title: "Write docs", done: true, priority: 2 },
  { title: "Fix bug", done: false, priority: 1 },
  { title: "Plan release", done: false, priority: 3 }
];

function createStatusFilter(showDone) {
  return function (task) {
    return showDone ? task.done : !task.done;
  };
}

function formatTask(task) {
  return task.priority + ": " + task.title;
}

const openTasks = tasks
  .filter(createStatusFilter(false))
  .map(formatTask);

console.log(openTasks); // ["1: Fix bug", "3: Plan release"]
```

Output (tested with Node 24.21.0):

```text
[ "1: Fix bug", "3: Plan release" ]
```

This mini project combines a function that returns a function with reusable callbacks. The result is a small pipeline that is easy to extend later.

## 10. Key Points

- Higher-order functions take functions as arguments, return functions, or both.
- JavaScript array methods like `map`, `filter`, and `reduce` are common examples.
- They make code more reusable by separating behavior from the code that uses it.
- They are often paired with callbacks and closures.
- Most beginner mistakes come from passing the wrong value, forgetting `return`, or losing object context.

## 11. Practice Exercise

Use the following task to check your understanding of higher-order functions.

- Create a function called `makeGreeter` that takes a title such as `"Dr."` and returns a function.
- The returned function should take a name and return a greeting like `"Hello, Dr. Rivera"`.
- Use the returned function twice with different names.

**Expected output:**

```javascript
Hello, Dr. Rivera
Hello, Dr. Chen
```

**Hint:** The outer function should return an inner function that uses the title from the outer scope.

**Solution:**

```javascript
function makeGreeter(title) {
  return function (name) {
    return "Hello, " + title + " " + name;
  };
}

const greetDoctor = makeGreeter("Dr.");

console.log(greetDoctor("Rivera"));
console.log(greetDoctor("Chen"));
```

Output (tested with Node 24.21.0):

```text
Hello, Dr. Rivera
Hello, Dr. Chen
```

## 12. Final Summary

Higher-order functions are functions that work with other functions as inputs or outputs. In JavaScript, this pattern appears everywhere, from array methods to custom utilities that wrap, configure, or transform behavior. Once you learn to recognize them, many common APIs become easier to understand.

They are especially powerful because they let you write small, focused functions and combine them into larger workflows. That leads to code that is easier to read, test, and reuse. If you want to go further, the next topic worth exploring is closures, since they explain how returned functions remember values from the outer scope.
