Higher-Order Functions
A higher-order function is a function that either takes other functions as arguments, returns a function, or both. Because functions are values in JavaScript, this is a first-class language feature, not a workaround. Higher-order functions are how you build expressive, reusable building blocks — map, filter, reduce, event handlers, middleware, debounce, and more are all higher-order.
The two shapes
Takes a function —
arr.map(fn)accepts a function to apply to each element.Returns a function —
makeAdder(5)returns a new functionadd5. Often used for "configured" or "specialised" helpers.
Built-in array higher-order methods
The most common higher-order functions are right there on Array.prototype. Each one takes a function and applies it across the array in some way.
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
const total = numbers.reduce((acc, n) => acc + n, 0);
const found = numbers.find(n => n > 3);
const allPos = numbers.every(n => n > 0);
const anyHuge = numbers.some(n => n > 100);
console.log({ doubled, evens, total, found, allPos, anyHuge });{
doubled: [ 2, 4, 6, 8, 10 ],
evens: [ 2, 4 ],
total: 15,
found: 4,
allPos: true,
anyHuge: false
}Why this is powerful — separation of concerns
Imagine writing the same logic with a for loop:
const doubled = [];
for (let i = 0; i < numbers.length; i++) {
doubled.push(numbers[i] * 2);
}Compare to numbers.map(n => n * 2). The higher-order version separates what to do (multiply by 2) from how to iterate (visit each element, collect results). You stop maintaining the loop boilerplate and focus on the actual transformation.
Composing transformations
Because each method returns a value, you can chain them into clear pipelines.
const users = [
{ name: "Ada", age: 36, active: true },
{ name: "Lin", age: 28, active: false },
{ name: "Pedro", age: 41, active: true },
];
const activeNames = users
.filter(u => u.active)
.map(u => u.name.toUpperCase());
console.log(activeNames); // [ 'ADA', 'PEDRO' ]Returning a function — factories
When a function returns another function, you can build specialised helpers from a general one.
function makeMultiplier(factor) {
return n => n * factor;
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);
console.log(double(7)); // 14
console.log(triple(7)); // 21This pattern is how bind, partial application, and currying work. The "remembered" factor lives in the closure (see Closures).
A real debounce
Debouncing — calling a function only after the user has stopped typing — is a classic higher-order pattern. It takes a function and returns a new function with extra timing logic.
function debounce(fn, ms) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), ms);
};
}
const log = (q) => console.log("searching for:", q);
const debouncedLog = debounce(log, 300);
debouncedLog("a");
debouncedLog("ap");
debouncedLog("app");
// After 300ms of silence, prints: searching for: appFunction composition
A natural extension: write a tiny helper that takes several functions and returns a new function that runs them in sequence.
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);
const trim = s => s.trim();
const lower = s => s.toLowerCase();
const dashed = s => s.replace(/\s+/g, "-");
const slug = compose(dashed, lower, trim);
console.log(slug(" Hello World ")); // "hello-world"Common higher-order patterns
map,filter,reduce— transform, narrow, summarise collections.debounce/throttle— wrap a function with timing behaviour.memoize— wrap a function with a cache.once— wrap a function so it only runs the first time.Middleware — Express, Redux, and many libraries are pipelines of higher-order functions.
Event handlers —
addEventListenertakes a function. Any time you pass a callback, you are using a higher-order API.