# Higher-Order Functions

## 1. Definition

A **higher-order function** is any function that does at least one of:
1. Takes another function as an argument, or
2. Returns a function as its result.

Functions are first-class values in JavaScript — they can be stored in variables, passed around, and returned, exactly like numbers or strings. This is what makes higher-order functions possible.

```js
function applyTwice(fn, value) {   // takes a function as an argument
  return fn(fn(value));
}
applyTwice(x => x * 2, 3); // 12

function multiplier(factor) {       // returns a function
  return x => x * factor;
}
const triple = multiplier(3);
triple(5); // 15
```

## 2. Built-in Higher-Order Functions You Already Use

`map`, `filter`, `reduce`, `forEach`, `sort` (with a comparator), `setTimeout`, and `addEventListener` are all higher-order functions — they accept a callback function as an argument.

```js
[1, 2, 3].map(n => n * 2);              // map is higher-order — takes a function
setTimeout(() => console.log("done"), 1000); // setTimeout is higher-order too
```

## 3. Function Composition

Combining small, single-purpose functions into a pipeline is the most practical use of higher-order functions:

```js
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);

const trim = s => s.trim();
const lower = s => s.toLowerCase();
const exclaim = s => s + "!";

const shout = pipe(trim, lower, exclaim);
shout("  HELLO  "); // "hello!"
```

`pipe` runs left to right; `compose` runs right to left — both are standard names in functional-programming-flavored JavaScript.

![pipe(trim, lower, exclaim) feeding "  HI  " through each function in sequence to produce "hi!"](diagrams/01-pipe-flow.svg)

## 4. Returning Functions: Configuration and Specialization

```js
function createValidator(minLength) {
  return function (value) {
    return typeof value === "string" && value.length >= minLength;
  };
}

const isValidPassword = createValidator(8);
isValidPassword("short");     // false
isValidPassword("longenough"); // true
```

This pattern — a function that returns a specialized function — is the basis of currying, decorators, and middleware chains (Express middleware and Redux middleware both rely on this exact shape).

## 5. Decorators (Wrapping Functions)

A higher-order function can wrap another function to add behavior without modifying its source:

```js
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); // logs the call, then returns 5
```

This is the same principle behind `memoize` (Closures topic), `debounce`, and `throttle`.

## 6. Debounce and Throttle — Practical Higher-Order Functions

```js
function debounce(fn, delayMs) {
  let timeoutId;
  return function (...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn.apply(this, args), delayMs);
  };
}
```

`debounce` returns a new function that resets its timer on every call, only firing `fn` once activity has paused — the standard fix for excessive `input`/`resize` event handling.

## 7. Under the Hood: Call-Site Monomorphism for Callbacks

The Functions topic's tiered compilation pipeline and the Prototype Chain topic's inline caches both apply directly to *callback* call sites — and this is where higher-order functions can quietly cost more than a hand-written loop, or, when the engine can optimize well, cost almost nothing extra at all.

### Inlining: erasing the function-call overhead entirely

When a call site like `arr.map(callback)` consistently receives the **same** `callback` function every time it runs, the optimizing compiler can **inline** that callback directly into `map`'s own optimized code — eliminating the function-call overhead altogether, so the generated machine code ends up close to what a manual `for` loop would produce.

```js
function double(x) { return x * 2; }
const result = [1, 2, 3].map(double); // same `double` function every time this line runs -> inlinable
```

### Megamorphic callback sites lose this optimization

```js
function runTransform(arr, transformFn) {
  return arr.map(transformFn); // `transformFn` is a DIFFERENT function object on every call
}
runTransform(data1, x => x + 1);
runTransform(data2, x => x * 2);
runTransform(data3, x => x.toUpperCase());
```

Here, the `.map()` call site inside `runTransform` sees a different callback identity every invocation — it can never safely assume "it's always this one function," so it can't inline, and falls back to a real indirect function call each time. This is the same monomorphic/polymorphic/megamorphic spectrum from the Prototype Chain topic, applied to *functions* as the varying thing instead of *object shapes*.

### Why this rarely matters, and when it does

For the overwhelming majority of code, this distinction is invisible — a few extra function-call frames per iteration is negligible next to real work (network calls, DOM updates, business logic). It becomes worth knowing specifically in **hot paths**: tight loops over large arrays, called extremely often, where a highly polymorphic higher-order pattern (a different closure created and passed in on every call, as `debounce`/`memoize`'s returned functions are) can prevent the kind of inlining that makes functional-style code as fast as imperative code in the first place.

### Closures returned from decorators are fresh objects every time

Every call to `debounce(fn, delayMs)` (section 6) or `memoize(fn)` allocates a brand-new function object with its own `Context` (see the Closures topic) — calling a decorator factory repeatedly (e.g. inside a loop, or on every render in a UI framework) creates a new, independent closure each time, none of which share state with each other, and each of which represents a fresh object the engine has to track from scratch for optimization purposes.

## 8. Common Pitfalls

1. **Losing `this` inside a returned function** — if the wrapped function relies on `this`, use `fn.apply(this, args)` or convert to arrow functions consistently (see the `this` keyword topic).
2. **Over-composing for its own sake** — a 6-function `pipe` chain that's only used once is often less readable than a plain sequence of statements; reserve composition for genuinely reused logic.
3. **Forgetting closures persist state between calls** — a decorator like `debounce` keeps `timeoutId` alive across every call via closure; creating a new debounced function on every render (e.g. inline in a React component) defeats the purpose because the closure resets each time.
4. **Confusing "takes a callback" with "is asynchronous"** — `map`/`filter`/`sort` are higher-order but fully synchronous; higher-order-ness and async-ness are unrelated concepts.

## 9. Quick Reference

```
higher-order function = takes a function in, OR returns a function out (or both)
pipe(f, g, h)(x)  = h(g(f(x)))     — left to right
compose(f, g, h)(x) = f(g(h(x)))   — right to left
```
