Suggest an editImprove this articleRefine the answer for “What are higher-order functions in JavaScript (hof)”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Higher-order function (HOF)** - a function that takes another function as an argument or returns a function as a result. The function you pass in is called a callback; the HOF is what receives or creates it. ```javascript // Takes a function as argument [1, 2, 3].map(x => x * 2); // [2, 4, 6] // Returns a function function multiplier(factor) { return (n) => n * factor; } const double = multiplier(2); // double(5) => 10 ``` **Key point:** `map`, `filter`, `reduce`, and `forEach` are all HOFs built into JavaScript.Shown above the full answer for quick recall.Answer (EN)Image**Higher-order function (HOF)** - a function that takes another function as an argument, returns a function as a result, or does both. ## Theory ### TL;DR - Functions in JavaScript are values - pass them around like numbers or strings - HOF = takes a function as input, or returns one, or both - Built-in examples you already know: `map`, `filter`, `reduce`, `forEach` - Use HOF when the same operation repeats with different behavior - pass the behavior in instead of copying code - Decision rule: if you catch yourself writing the same loop with a slightly different operation inside, a HOF probably belongs there ### Quick example ```javascript // map() is a HOF - accepts a function, applies it to each element const numbers = [1, 2, 3]; const doubled = numbers.map(num => num * 2); // [2, 4, 6] // multiplier() is a HOF - returns a new configured function function multiplier(factor) { return function(number) { return number * factor; }; } const double = multiplier(2); console.log(double(5)); // 10 ``` `map` takes `num => num * 2` as an argument. `multiplier` builds and returns a new function configured with `factor`. Both are HOFs. ### Why HOFs exist Without HOFs, you repeat logic. With them, you write it once and pass in what changes. That is the whole point. Consider transforming an array: without `map`, you write a `for` loop every time, declare a new array, manually push results. With `map`, you say "here is what to do per item" and it handles the rest. The operation stays consistent; the behavior you inject varies. I have seen developers copy-paste the same `for` loop five times with slightly different transformations inside. A `map` call would have been one line each time. This works because JavaScript treats functions as first-class citizens. They are objects stored on the heap, assignable to variables, passable as arguments, returnable from other functions. A HOF just uses that deliberately. ### When to use - **Transforming collections:** `map`, `filter`, `reduce` for any list of API data or UI items - **Event handling:** `addEventListener` takes a callback - your function runs when the event fires - **Async flows:** `Promise.then()` takes a function to run on resolve - **Express middleware:** each middleware is a HOF that wraps the next handler with extra logic - **Specialized functions:** `multiplier(2)` gives you `double`, `multiplier(3)` gives you `triple`, from one definition For simple one-off logic where a plain loop reads more clearly, skip the HOF. ### How it works internally V8 treats functions as objects. When you pass a function to a HOF, V8 copies a reference to that object, not the code itself. When a HOF returns a function, the returned function captures the outer scope through a closure - V8 allocates a closure cell on the heap holding references to lexical variables, keeping them alive as long as the inner function lives. Built-in methods like `Array.prototype.map` are implemented in C++ and call your JS callback on each iteration. ### Common mistakes **Forgetting to call the returned function:** ```javascript const doubler = multiplier(2); // Returns a function console.log(multiplier(2)); // Logs [Function] - not a number! console.log(doubler(5)); // 10 - this is what you wanted ``` `multiplier(2)` hands you a function. You still need to call it with the actual value. **Mutating the passed function:** ```javascript // Bad: adds a property to the original function object function badHOF(fn) { fn.customProp = 'mutated'; // Changes caller scope return fn; } // Fine: wrap it, leave the original alone function goodHOF(fn) { return (...args) => fn(...args); } ``` Functions are objects. Adding properties to them affects the original outside your HOF. **Losing `this` context in callbacks:** ```javascript const obj = { value: 42 }; [1, 2].forEach(function() { console.log(this.value); // undefined - context is lost }); [1, 2].forEach(function() { console.log(this.value); // 42 - explicit binding restores it }.bind(obj)); ``` **Unstable HOF references in React deps:** ```javascript // Bad: new function reference every render triggers effect each time useEffect(() => { fetchData(increment); }, [increment]); // Good: useCallback stabilizes the reference const increment = useCallback(() => setCount(c => c + 1), []); ``` React compares dependencies by reference. A new function each render looks like a changed dependency and re-runs the effect. ### Real-world usage - `Array.map/filter/reduce`: every React list render, Lodash data pipelines - Express middleware: `app.use(authMiddleware(handler))` - each middleware wraps the next handler - `Promise.then()`: chains async work by passing handlers - React `useCallback`: memoizes a function to keep its reference stable between renders - Lodash `_.curry` / `_.partial`: build specialized functions from general ones ### Follow-up questions **Q:** What is the difference between a HOF and a callback? **A:** A callback is any function passed as an argument. A HOF is the function that receives it. `map` is the HOF; `num => num * 2` is the callback. **Q:** Implement `map` from scratch. **A:** ```javascript function myMap(arr, fn) { const result = []; for (let i = 0; i < arr.length; i++) { result.push(fn(arr[i], i, arr)); } return result; } ``` **Q:** How do closures relate to HOFs? **A:** Most HOFs that return functions rely on closures. The returned function keeps access to variables from the outer scope - like `factor` inside `multiplier` - even after the outer call has finished. **Q:** Do HOFs have a performance cost? **A:** In tight loops, yes. Returned functions create closure allocations on the heap. For 1M+ items, a manual loop can run 2-3x faster in V8. In most application code that threshold does not matter. **Q:** In React 18 concurrent mode, why does an un-memoized HOF in `useEffect` deps cause an infinite loop? **A:** Each render creates a new function reference. React compares dependencies by reference, so a new function looks like a changed dep. The effect re-runs, triggers another render, which creates another new function. `useCallback` with a correct deps array breaks the cycle. ## Examples ### Basic: wrapping any function with a logger ```javascript function withLogging(fn) { return (...args) => { console.log('Calling:', fn.name, 'with args:', args); const result = fn(...args); console.log('Returned:', result); return result; }; } function add(a, b) { return a + b; } const loggedAdd = withLogging(add); loggedAdd(3, 4); // Calling: add with args: [3, 4] // Returned: 7 ``` `withLogging` returns a new function that wraps whatever you pass in. The original `add` is untouched. This is the decorator pattern: extend behavior without changing the source function. ### Intermediate: Express route validation middleware ```javascript const express = require('express'); const app = express(); app.use(express.json()); // HOF: accepts a route handler, returns a new handler with validation built in function validateUser(fn) { return (req, res, next) => { if (!req.body.username) { return res.status(400).send('Missing username'); } return fn(req, res, next); }; } const userHandler = (req, res) => res.json({ user: req.body.username }); app.post('/user', validateUser(userHandler)); // POST /user { username: 'alice' } => 200 { user: 'alice' } // POST /user {} => 400 "Missing username" ``` `validateUser` wraps any route handler with a validation check. The handler itself knows nothing about validation. Swap in `requireAdmin` or `checkRateLimit` and the pattern is identical.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.