What are higher-order functions in JavaScript?
Advertisement
🧩 Scenario
Architecture Walkthrough
What Makes a Function Higher-Order
A higher-order function satisfies at least one of two conditions: it accepts one or more functions as arguments, or it returns a function as its result. This definition is possible in JavaScript because functions are first-class values: they can be stored in variables, passed as arguments, and returned from other functions exactly like any other value.
The built-in array methods map, filter, reduce, forEach, find, and sort are all higher-order functions. Each one accepts a callback, which is itself a function. The pattern is so common in JavaScript that developers use higher-order functions constantly without explicitly naming them as such.
Decorators and Function Wrapping
A common use of higher-order functions is wrapping an existing function to add behavior without modifying the original. This is the decorator pattern. A decorator accepts a function, returns a new function that calls the original, and adds logic before or after the call (logging, timing, validation, retry logic).
This pattern is the foundation of Express middleware, where each middleware is effectively a decorator over the request handling pipeline. It is also the basis of React Higher-Order Components (HOCs), where a component is wrapped by a function that adds behavior and returns an enhanced component.
Partial Application and Currying
Higher-order functions that return functions enable partial application: fixing some arguments of a function up front and receiving a new function that accepts the remaining arguments. This is useful when a function is called repeatedly with some arguments the same and others varying.
Currying is a specific form of partial application where a multi-argument function is transformed into a chain of single-argument functions. Libraries like Ramda and Lodash provide curry utilities. In practice, many developers use partial application manually through closures rather than full currying, since JavaScript is not a curried-by-default language.
Key Code Explained
// Higher-order function: accepts a function as an argument
function withLogging(fn) {
return function (...args) {
console.log(`Calling ${fn.name} with:`, args);
const result = fn(...args);
console.log(`${fn.name} returned:`, result);
return result;
};
}
const add = (a, b) => a + b;
const loggedAdd = withLogging(add);
loggedAdd(2, 3);
// Calling add with: [2, 3]
// add returned: 5
// Partial application via closure
function multiply(factor) {
return (n) => n * factor;
}
const double = multiply(2);
const triple = multiply(3);
double(5); // 10
triple(5); // 15
// Implementing a simplified Array.map
function myMap(arr, transform) {
const result = [];
for (const item of arr) {
result.push(transform(item));
}
return result;
}
myMap([1, 2, 3], (n) => n * 2); // [2, 4, 6]
// Simple memoize: cache results by argument
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn(...args);
cache.set(key, result);
return result;
};
}
The memoize function is a real-world higher-order function that illustrates all three aspects: it accepts a function, uses a closure to maintain state between calls, and returns a new function with enhanced behavior. It is worth knowing because React's useMemo and useCallback hooks implement the same concept.
Tradeoffs
| Pattern | Pro | Con |
|---|---|---|
| Decorator/wrapper HOF | Adds behavior without modifying the original function | Each call creates a new wrapper; may obscure the original function name in stack traces |
| Partial application | Simplifies repeated calls with shared arguments | Can be confusing to readers unfamiliar with the pattern |
| Direct implementation | Clear and explicit, no indirection | Repeating logic across multiple functions violates DRY |
What Interviewers Actually Check
- Whether you can give the correct two-part definition without confusing it with "a function that calls another function"
- Whether you recognize built-in array methods as higher-order functions and can explain why
- Whether you can write a function decorator that wraps without mutating the original
- Whether you understand partial application and can write a basic example
- Whether you can name a real-world pattern (middleware, HOC, memoize) and connect it to the higher-order function concept
Follow-Up Questions
- How does the middleware pattern in Express.js use higher-order functions?
- What is the difference between a Higher-Order Component in React and a custom hook, and when would you choose one over the other?
- How does
Function.prototype.bindrelate to partial application? - If a higher-order function wraps an async function, what must the wrapper do to correctly propagate the returned Promise?
- A team utility function wraps every API call in a retry higher-order function. What edge cases would you think about before approving it?
Common Candidate Mistakes
- Saying "a function that calls another function" as the definition, which describes many functions but is not the correct definition of a higher-order function
- Not recognizing
Array.map,filter, andreduceas higher-order functions - Forgetting that returning a function also qualifies: partial application and factory functions are higher-order functions too
- Writing a decorator that mutates the original function reference rather than returning a new function
- Not preserving the original function's
thiscontext inside a higher-order wrapper, breaking method calls
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you give the two-part definition of a higher-order function without using vague language?
- Can you write a function decorator that adds logging without changing the wrapped function's behavior?
- Can you explain how
Array.mapis a higher-order function and implement a simplified version? - Can you explain partial application and write a simple example?
- Can you name two real-world patterns that rely on higher-order functions?
Summary
A higher-order function is a function that either accepts another function as an argument, returns a function as its result, or both. This is possible in JavaScript because functions are first-class values that can be passed and returned exactly like numbers or strings.
Built-in array methods, Express middleware, React HOCs, Redux enhancers, and utility decorators like memoize and throttle all follow the higher-order function pattern. Recognizing it lets you understand very different-looking APIs as variations on the same underlying idea.
The practical value of higher-order functions is composability: they let you add or remove behavior (logging, caching, retrying, validating) without modifying the underlying function. This makes code more modular and easier to test in isolation.
Are all array methods higher-order functions?
Yes, methods like map, filter, reduce, and forEach are all higher-order functions because they accept a callback.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement