[{"data":1,"prerenderedAt":178},["ShallowReactive",2],{"qa-\u002Fjavascript\u002Ffunctions\u002Fhigher-order-functions":3},{"page":4,"siblings":158,"blog":175},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"related":149,"seo":150,"seoDescription":151,"stem":152,"subtopic":153,"topic":154,"topicSlug":155,"updated":156,"__hash__":157},"qa\u002Fjavascript\u002Ffunctions\u002Fhigher-order-functions.md","Higher Order Functions",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","JavaScript","javascript",{},true,3,"\u002Fjavascript\u002Ffunctions\u002Fhigher-order-functions",[23,28,32,36,40,44,48,52,56,60,65,69,73,77,81,85,89,93,97,101,105,109,113,117,121,125,129,133,137,141,145],{"id":24,"difficulty":25,"q":26,"a":27},"what-is-hof","easy","What is a higher-order function?","A **higher-order function (HOF)** is a function that does at least one of two\nthings: **takes one or more functions as arguments**, or **returns a\nfunction**. Any function that only deals with plain values is *first-order*.\n\n```js\n\u002F\u002F takes a function (callback)\n[1, 2, 3].map(n => n * 2)              \u002F\u002F map is a HOF\n\n\u002F\u002F returns a function\nconst multiplier = factor => n => n * factor  \u002F\u002F HOF returning a HOF\nconst double = multiplier(2)\ndouble(5) \u002F\u002F 10\n```\n\nHOFs are possible because functions in JavaScript are **first-class values** —\nthey can be stored, passed, and returned like any other value. They are the\nbackbone of functional-style JS and enable reuse without copy-pasting logic.\n",{"id":29,"difficulty":25,"q":30,"a":31},"what-is-callback","What is a callback function?","A **callback** is a function passed into another function so it can be\n**called back later** — either synchronously (now) or asynchronously (later).\n\n```js\n\u002F\u002F synchronous callback\n[1, 2, 3].forEach(n => console.log(n))   \u002F\u002F called once per element, now\n\n\u002F\u002F asynchronous callback\nsetTimeout(() => console.log('later'), 0) \u002F\u002F called after the timer fires\n```\n\nThe function receiving the callback decides **when** and **with what\narguments** to invoke it. A common gotcha: a synchronous-looking API may call\nthe callback asynchronously, so don't assume code after the call has the\ncallback's results yet.\n",{"id":33,"difficulty":25,"q":34,"a":35},"map-as-hof","How is Array.prototype.map a higher-order function?","`map` is a HOF because it **accepts a function** and applies it to every\nelement, returning a **new array** of the results. It abstracts the loop.\n\n```js\nconst nums = [1, 2, 3]\nconst squared = nums.map(n => n * n)   \u002F\u002F [1, 4, 9]\n\u002F\u002F nums is unchanged — map does not mutate\n```\n\nThe callback receives `(element, index, array)`. **Pitfall:** `map` is for\ntransforming into a new array — if you only need side effects (logging,\npushing to the DOM) use `forEach` instead, otherwise you allocate an array of\n`undefined` you never use.\n",{"id":37,"difficulty":25,"q":38,"a":39},"filter-as-hof","What does Array.prototype.filter do and why is it a HOF?","`filter` is a HOF that takes a **predicate** (a function returning a boolean)\nand returns a **new array** containing only the elements for which the\npredicate is truthy.\n\n```js\nconst nums = [1, 2, 3, 4]\nconst evens = nums.filter(n => n % 2 === 0)  \u002F\u002F [2, 4]\n```\n\nThe result's **truthiness** is what matters, not strict `true`. **Pitfall:**\nreturning a non-boolean accidentally — e.g. `filter(n => n.id)` keeps elements\nwith truthy `id`, which silently drops items whose id is `0` or `''`.\n",{"id":41,"difficulty":14,"q":42,"a":43},"reduce-as-hof","Explain reduce as a higher-order function.","`reduce` is the most general array HOF: it takes a **reducer function**\n`(accumulator, element) => newAccumulator` plus an initial value, and folds\nthe whole array down to a **single value** (which can itself be an array or\nobject).\n\n```js\nconst sum = [1, 2, 3, 4].reduce((acc, n) => acc + n, 0)  \u002F\u002F 10\n\n\u002F\u002F map and filter can both be expressed via reduce\nconst doubled = [1, 2, 3].reduce((acc, n) => [...acc, n * 2], []) \u002F\u002F [2,4,6]\n```\n\n**Pitfall:** omitting the initial value. Without it, the first element becomes\nthe accumulator and iteration starts at index 1 — and `reduce` on an **empty\narray with no initial value throws** a TypeError.\n",{"id":45,"difficulty":14,"q":46,"a":47},"pure-vs-callback","Why do HOFs like map\u002Ffilter\u002Freduce make code more reusable?","They **separate the iteration mechanism from the per-element logic**. The HOF\nowns the loop, bounds checking, and array allocation; you supply only the\nsmall piece that varies. This is the **template method** idea expressed with\nfunctions.\n\n```js\n\u002F\u002F imperative: loop boilerplate repeated everywhere\nconst out = []\nfor (let i = 0; i \u003C users.length; i++) out.push(users[i].name)\n\n\u002F\u002F declarative: intent is obvious, no boilerplate\nconst names = users.map(u => u.name)   \u002F\u002F\n```\n\nBecause the callback is just a value, you can name it, test it in isolation,\nand reuse it across many call sites — reducing duplication and bugs.\n",{"id":49,"difficulty":14,"q":50,"a":51},"function-composition","What is function composition?","**Composition** combines small functions into a bigger one, where the output\nof each becomes the input of the next — `compose(f, g)(x) === f(g(x))`.\n\n```js\nconst compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x)\nconst pipe    = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x)\n\nconst clean = pipe(s => s.trim(), s => s.toLowerCase())\nclean('  HELLO ')  \u002F\u002F 'hello'   reads left-to-right\n```\n\n`compose` runs **right-to-left** (math convention); `pipe` runs\n**left-to-right** (reads like a pipeline). **Pitfall:** each function should\ntake and return a single value — composition breaks down with multi-arg\nfunctions unless you curry them first.\n",{"id":53,"difficulty":14,"q":54,"a":55},"what-is-currying","What is currying?","**Currying** transforms a function of N arguments into N nested functions that\neach take **one argument** and return the next function until all args are\ncollected.\n\n```js\nconst add = a => b => c => a + b + c   \u002F\u002F curried\nadd(1)(2)(3)   \u002F\u002F 6\n\n\u002F\u002F vs the normal form\nconst addN = (a, b, c) => a + b + c\naddN(1, 2, 3)  \u002F\u002F 6\n```\n\nCurrying enables **partial application** and clean composition, because each\nstage returns a specialized function. **Pitfall:** deeply curried APIs hurt\nreadability and are harder to debug — reserve currying for cases where the\npartial-application benefit is real.\n",{"id":57,"difficulty":14,"q":58,"a":59},"partial-application","What is partial application and how does it differ from currying?","**Partial application** fixes *some* of a function's arguments now, producing a\nnew function that takes the *rest* later. **Currying** is stricter: it always\nbreaks a function into a chain of **one-argument** functions.\n\n```js\nconst greet = (greeting, name) => `${greeting}, ${name}!`\nconst hi = greet.bind(null, 'Hi')   \u002F\u002F partial application via bind\nhi('Sam')  \u002F\u002F 'Hi, Sam!'   greeting already fixed\n```\n\nSo every curried call is a partial application, but partial application can\nfix several arguments at once and isn't restricted to one-at-a-time. `bind` is\nthe built-in tool for it (ignoring the `this` argument here with `null`).\n",{"id":61,"difficulty":62,"q":63,"a":64},"point-free-style","hard","What is point-free (tacit) style?","**Point-free style** defines functions **without naming their arguments**,\nbuilding them by composing other functions instead. The \"point\" is the data\nargument you don't mention.\n\n```js\n\u002F\u002F pointed\nconst isOdd = n => n % 2 === 1\nconst countOdds = arr => arr.filter(isOdd).length\n\n\u002F\u002F point-free helper composed from smaller pieces\nconst prop = key => obj => obj[key]\nconst names = users.map(prop('name'))   \u002F\u002F no obj parameter named\n```\n\nIt can read very cleanly, but **pitfall:** taken too far it becomes cryptic\nand stack traces lose meaningful names. Use it where it genuinely clarifies.\n",{"id":66,"difficulty":14,"q":67,"a":68},"what-is-memoization","What is memoization and how do you implement it with a HOF?","**Memoization** caches a pure function's results keyed by its arguments, so\nrepeated calls with the same input return instantly instead of recomputing.\nA HOF wraps the original function and keeps the cache in a **closure**.\n\n```js\nfunction memoize(fn) {\n  const cache = new Map()\n  return function (arg) {\n    if (cache.has(arg)) return cache.get(arg)   \u002F\u002F cache hit\n    const result = fn.call(this, arg)\n    cache.set(arg, result)\n    return result\n  }\n}\n```\n\n**Pitfall:** it only works for **pure** functions (same input -> same output)\nand the naive version keys on a single primitive arg — multi-arg or object\nkeys need a serialization strategy and risk unbounded memory growth.\n",{"id":70,"difficulty":14,"q":71,"a":72},"debounce-as-hof","How would you implement debounce, and why is it a HOF?","**Debounce** returns a wrapped function that delays calling the original until\nactivity has **stopped for a quiet period**; each new call resets the timer.\nIt's a HOF: it takes a function and returns a closure holding the timer.\n\n```js\nfunction debounce(fn, wait) {\n  let timer\n  return function (...args) {\n    clearTimeout(timer)\n    timer = setTimeout(() => fn.apply(this, args), wait)   \u002F\u002F resets\n  }\n}\nconst onSearch = debounce(query => fetchResults(query), 300)\n```\n\nGreat for search-as-you-type or resize handlers. **Pitfall:** the wrapper must\nforward `this` and `args` with `apply`, or the debounced handler loses its\ncontext and the event arguments.\n",{"id":74,"difficulty":14,"q":75,"a":76},"throttle-as-hof","What is throttle and how does it differ from debounce?","**Throttle** guarantees the function runs **at most once per interval**, no\nmatter how often it's called — useful for scroll or mousemove. **Debounce**\ninstead waits until calls *stop*. Both are HOFs returning a stateful closure.\n\n```js\nfunction throttle(fn, interval) {\n  let last = 0\n  return function (...args) {\n    const now = Date.now()\n    if (now - last >= interval) {        \u002F\u002F fire on leading edge\n      last = now\n      fn.apply(this, args)\n    }\n  }\n}\n```\n\n**Rule of thumb:** throttle for *steady sampling* during continuous events;\ndebounce for *\"do it once they're done\"* events.\n",{"id":78,"difficulty":14,"q":79,"a":80},"once-hof","How do you implement a once() higher-order function?","`once` returns a wrapper that lets the underlying function run **only the\nfirst time**; later calls return the cached first result and skip execution.\nThe \"have I run yet\" flag and result live in a closure.\n\n```js\nfunction once(fn) {\n  let called = false, result\n  return function (...args) {\n    if (!called) {\n      called = true\n      result = fn.apply(this, args)   \u002F\u002F runs exactly once\n    }\n    return result\n  }\n}\nconst init = once(() => console.log('setup'))\ninit(); init()   \u002F\u002F logs 'setup' only once\n```\n\nHandy for idempotent initialization. **Pitfall:** the result is cached forever\n— if the first call threw, decide whether `called` should stay false to allow\na retry.\n",{"id":82,"difficulty":14,"q":83,"a":84},"returning-functions","Why would a function return another function?","Returning a function lets you **pre-configure behavior now and defer\nexecution to later**, capturing the configuration in a closure. This is the\nmechanism behind factories, currying, and middleware.\n\n```js\nfunction makeTagger(tag) {\n  return content => `\u003C${tag}>${content}\u003C\u002F${tag}>`   \u002F\u002F remembers `tag`\n}\nconst h1 = makeTagger('h1')\nh1('Hello')   \u002F\u002F '\u003Ch1>Hello\u003C\u002Fh1>'   tag captured\n```\n\nThe returned function is **specialized** without you rewriting it. The key\nenabler is the closure: each returned function keeps its own copy of the\ncaptured configuration.\n",{"id":86,"difficulty":14,"q":87,"a":88},"callbacks-sync-async","What's the difference between synchronous and asynchronous callbacks?","A **synchronous callback** runs to completion *before* the HOF returns (like\n`map`'s callback). An **asynchronous callback** is scheduled and runs *later*,\nafter the current call stack clears (like `setTimeout` or a fetch handler).\n\n```js\nconsole.log('A')\n[1].forEach(() => console.log('B'))     \u002F\u002F sync -> B before C\nsetTimeout(() => console.log('D'), 0)   \u002F\u002F async -> D last\nconsole.log('C')\n\u002F\u002F order: A, B, C, D\n```\n\n**Pitfall:** mixing them in one API (\"sometimes sync, sometimes async\") causes\nsubtle bugs — choose one contract. This is also why callback-based async code\nled to \"callback hell\" and motivated Promises.\n",{"id":90,"difficulty":62,"q":91,"a":92},"map-parseint-gotcha","Why does ['1','2','3'].map(parseInt) not return [1, 2, 3]?","`map` calls the callback with **three** arguments — `(value, index, array)` —\nbut `parseInt(string, radix)` reads the **second** as a radix. So you actually\ncall `parseInt('1', 0)`, `parseInt('2', 1)`, `parseInt('3', 2)`.\n\n```js\n['1', '2', '3'].map(parseInt)\n\u002F\u002F parseInt('1', 0) -> 1   (radix 0 ignored -> base 10)\n\u002F\u002F parseInt('2', 1) -> NaN (radix 1 invalid)\n\u002F\u002F parseInt('3', 2) -> NaN ('3' not a binary digit)\n\u002F\u002F result: [1, NaN, NaN]\n```\n\n**Fix:** wrap it so only the value is passed:\n`['1','2','3'].map(s => parseInt(s, 10))` -> `[1, 2, 3]`. The lesson: be\ncareful passing a multi-arity built-in directly as a callback.\n",{"id":94,"difficulty":62,"q":95,"a":96},"losing-this-method-ref","Why does passing a method reference as a callback often lose `this`?","Passing `obj.method` extracts the **function value alone** — the `obj`\nreceiver is *not* attached. When the HOF later calls it as a plain function,\n`this` is `undefined` (strict) or the global object.\n\n```js\nconst counter = {\n  count: 0,\n  inc() { this.count++ }\n}\n[1, 2].forEach(counter.inc)   \u002F\u002F `this` is not `counter`\n```\n\n**Fixes:** bind it — `forEach(counter.inc.bind(counter))` — or wrap in an\narrow that preserves the receiver — `forEach(() => counter.inc())`. The\nroot cause is that `this` is determined by **how** a function is called, not\nwhere it was defined.\n",{"id":98,"difficulty":14,"q":99,"a":100},"composition-vs-chaining","How does method chaining relate to function composition?","**Method chaining** (`arr.filter(...).map(...).reduce(...)`) is composition\nwhere each step is a **method returning a chainable value**. Standalone\n**composition** (`pipe(f, g, h)`) works on free functions and any data type,\nnot just arrays.\n\n```js\n\u002F\u002F chaining\nconst total = orders.filter(o => o.paid).map(o => o.amount)\n                    .reduce((a, b) => a + b, 0)   \u002F\u002F reads top-to-bottom\n\n\u002F\u002F composition with free functions\nconst total2 = pipe(\n  os => os.filter(o => o.paid),\n  os => os.map(o => o.amount),\n  os => os.reduce((a, b) => a + b, 0)\n)(orders)\n```\n\nChaining is ergonomic when the methods exist; composition is more general and\ndecouples logic from the data's prototype. **Pitfall:** long chains over large\narrays create many intermediate arrays.\n",{"id":102,"difficulty":14,"q":103,"a":104},"flatmap-hof","What does flatMap do and when is it useful?","`flatMap` is a HOF that **maps then flattens one level** in a single pass. It's\nequivalent to `arr.map(fn).flat()` but more efficient and expressive.\n\n```js\nconst sentences = ['hello world', 'foo bar']\nconst words = sentences.flatMap(s => s.split(' '))\n\u002F\u002F ['hello', 'world', 'foo', 'bar']   flattened\n\n\u002F\u002F also handy to map-and-filter: return [] to drop\n[1, 2, 3].flatMap(n => n % 2 ? [n] : [])  \u002F\u002F [1, 3]\n```\n\n**Pitfall:** it only flattens **one** level — deeply nested results need\n`flat(Infinity)` afterwards.\n",{"id":106,"difficulty":25,"q":107,"a":108},"every-some-hof","How do every() and some() use callbacks?","Both are HOFs taking a **predicate**. `every` returns `true` only if the\npredicate passes for **all** elements; `some` returns `true` if **at least\none** passes. Both **short-circuit**.\n\n```js\n[2, 4, 6].every(n => n % 2 === 0)  \u002F\u002F true\n[1, 2, 3].some(n => n > 2)         \u002F\u002F true (stops at 3)\n```\n\n`every` stops at the first falsy result; `some` stops at the first truthy\nresult. **Edge case:** `[].every(...)` is `true` (vacuous truth) and\n`[].some(...)` is `false`.\n",{"id":110,"difficulty":62,"q":111,"a":112},"bind-vs-arrow-callback","When wiring a callback, should you use bind or an arrow wrapper?","Both fix the receiver. `bind` creates a **new bound function once**, ideal when\nyou need a **stable reference** (e.g. to later `removeEventListener`). An arrow\nwrapper is created fresh each render\u002Fcall and is convenient for forwarding\nchanging arguments.\n\n```js\n\u002F\u002F stable, removable\nconst handler = this.onClick.bind(this)\nel.addEventListener('click', handler)\nel.removeEventListener('click', handler)   \u002F\u002F same reference\n\n\u002F\u002F arrow re-created each time -> cannot remove\nel.addEventListener('click', () => this.onClick())  \u002F\u002F no handle to remove\n```\n\n**Pitfall:** binding inside JSX\u002Frender or in a loop creates a new function\nevery time, which can defeat memoization and break listener removal.\n",{"id":114,"difficulty":14,"q":115,"a":116},"identity-and-constant","What are identity and constant functions and why are they useful in HOF code?","`identity = x => x` returns its argument unchanged; `constant = x => () => x`\nreturns a function that always yields `x`. They are tiny **building blocks**\nthat make HOF pipelines uniform.\n\n```js\nconst identity = x => x\n\u002F\u002F filter out falsy values with no custom predicate\n['a', '', 'b', null].filter(identity)   \u002F\u002F ['a', 'b']\n\nconst always5 = (() => () => 5)()\n[1, 2, 3].map(always5)   \u002F\u002F [5, 5, 5]\n```\n\nThey shine as **default callbacks** (e.g. a sort key defaulting to identity)\nand in composition where you need a no-op transform.\n",{"id":118,"difficulty":62,"q":119,"a":120},"compose-data-last","Why do functional libraries favor \"data-last\" argument order?","**Data-last** means the data parameter comes *last*, so the earlier\nconfiguration args can be **partially applied** to build reusable, composable\nfunctions. The data flows in only at the end of a pipeline.\n\n```js\nconst map = fn => arr => arr.map(fn)        \u002F\u002F data (arr) last\nconst filter = pred => arr => arr.filter(pred)\n\nconst process = pipe(filter(x => x > 0), map(x => x * 2))\nprocess([-1, 2, 3])   \u002F\u002F [4, 6]   data supplied last\n```\n\nNative array methods are **data-first** (`arr.map(fn)`), which is why you\noften wrap them. The trade-off: data-last composes beautifully but reads\nbackwards from the method style most JS developers know.\n",{"id":122,"difficulty":14,"q":123,"a":124},"callback-error-handling","What is the \"error-first\" callback convention?","The Node-style **error-first callback** convention passes the error as the\n**first argument** and the result(s) afterward: `callback(err, data)`. If\n`err` is truthy, something failed.\n\n```js\nfs.readFile('x.txt', (err, data) => {\n  if (err) return handle(err)   \u002F\u002F check error first\n  use(data)\n})\n```\n\n**Pitfall:** forgetting to `return` after handling the error lets the success\npath run with `undefined` data. This convention predates Promises, which\nlargely replaced it with `.then\u002F.catch` and `async\u002Fawait`.\n",{"id":126,"difficulty":14,"q":127,"a":128},"hof-this-arg","How do you set `this` for an array HOF callback without binding?","Many array HOFs accept an optional **`thisArg`** as their last parameter. When\nprovided (and the callback is a regular function, not an arrow), `this` inside\nthe callback is set to it.\n\n```js\nconst ctx = { factor: 3 }\n[1, 2, 3].map(function (n) { return n * this.factor }, ctx)  \u002F\u002F [3, 6, 9]\n```\n\n**Pitfall:** `thisArg` is **ignored by arrow callbacks**, because arrows take\n`this` lexically. Most modern code just uses an arrow that closes over the\nvalue instead of relying on `thisArg`.\n",{"id":130,"difficulty":62,"q":131,"a":132},"composing-async","How do you compose asynchronous functions?","Plain `compose`\u002F`pipe` assume synchronous functions. For async, each step\nreturns a Promise, so you **await between stages** — often by reducing over an\ninitial resolved Promise.\n\n```js\nconst pipeAsync = (...fns) => x =>\n  fns.reduce((acc, fn) => acc.then(fn), Promise.resolve(x))\n\nconst run = pipeAsync(fetchUser, u => fetchPosts(u.id), posts => posts.length)\nrun(42).then(console.log)   \u002F\u002F each step awaits the previous\n```\n\n**Pitfall:** mixing sync and async functions in one pipe is fine here because\n`then` auto-wraps non-Promise returns — but an unhandled rejection in any\nstage rejects the whole chain, so attach a `.catch`.\n",{"id":134,"difficulty":62,"q":135,"a":136},"curry-auto","How would you write a generic curry() helper?","A generic `curry` collects arguments until it has received as many as the\nfunction's **declared arity** (`fn.length`), then invokes it. Until then it\nreturns a function that accumulates more args.\n\n```js\nfunction curry(fn) {\n  return function curried(...args) {\n    if (args.length >= fn.length) return fn.apply(this, args)\n    return (...next) => curried.apply(this, [...args, ...next])   \u002F\u002F\n  }\n}\nconst add = curry((a, b, c) => a + b + c)\nadd(1)(2)(3)   \u002F\u002F 6\nadd(1, 2)(3)   \u002F\u002F 6   — flexible grouping\n```\n\n**Pitfall:** it relies on `fn.length`, which **excludes** rest params and\nparameters with defaults — so currying variadic functions doesn't work without\npassing an explicit arity.\n",{"id":138,"difficulty":62,"q":139,"a":140},"hof-reuse-vs-inheritance","How do HOFs offer an alternative to class inheritance for sharing behavior?","Instead of subclassing to share behavior, you can **wrap functions with HOFs**\n(decorators) to add cross-cutting concerns like logging, caching, or retries —\n**composition over inheritance**.\n\n```js\nconst withLogging = fn => (...args) => {\n  console.log('call', fn.name, args)\n  return fn(...args)\n}\nconst withRetry = fn => async (...args) => {\n  try { return await fn(...args) } catch { return fn(...args) }\n}\nconst robustFetch = withLogging(withRetry(fetchData))   \u002F\u002F stacked\n```\n\nEach concern is an independent, testable HOF you can stack in any order. This\navoids deep inheritance hierarchies and the fragile base-class problem.\n",{"id":142,"difficulty":14,"q":143,"a":144},"tap-debugging","What is a tap() function and how does it help debug pipelines?","`tap` runs a **side effect** (like logging) on a value and then **returns the\nvalue unchanged**, so you can drop it into a composition without altering the\ndata flow.\n\n```js\nconst tap = fn => x => { fn(x); return x }\n\nconst result = pipe(\n  x => x + 1,\n  tap(x => console.log('after +1:', x)),   \u002F\u002F peeks, passes through\n  x => x * 2\n)(5)   \u002F\u002F logs 6, result 12\n```\n\nIt's the functional equivalent of a breakpoint. **Pitfall:** because it's for\nside effects, don't accidentally `return fn(x)` — that would replace the value\nand corrupt the pipeline.\n",{"id":146,"difficulty":62,"q":147,"a":148},"avoid-shared-mutable-closure","What bug can a HOF cause by sharing mutable state across returned functions?","If a HOF closes over a **shared mutable variable** and returns functions that\nall reference it, they can interfere with each other — the classic loop-closure\ntrap.\n\n```js\nfunction makeHandlers() {\n  const fns = []\n  for (var i = 0; i \u003C 3; i++) fns.push(() => i)   \u002F\u002F all see final i\n  return fns\n}\nmakeHandlers().map(f => f())   \u002F\u002F [3, 3, 3]\n```\n\n**Fix:** use `let` (per-iteration binding) so each closure captures its own\n`i` -> `[0, 1, 2]`. The deeper lesson: when a HOF returns multiple closures,\nbe deliberate about whether they should **share** or **own** their captured\nstate.\n",null,{"description":11},"JavaScript higher-order function interview questions — callbacks, map\u002Ffilter\u002F reduce, composition, currying, partial application, memoization, debounce and throttle, plus classic callback pitfalls.","javascript\u002Ffunctions\u002Fhigher-order-functions","Higher-Order Functions","Functions","functions","2026-06-18","gajpsA9HuJ91gZ5frBbNlC6sHDnB7KjdH7Q19PqI4FY",[159,163,166,167,171],{"subtopic":160,"path":161,"order":162},"Closures","\u002Fjavascript\u002Ffunctions\u002Fclosures",1,{"subtopic":164,"path":165,"order":12},"The this Keyword","\u002Fjavascript\u002Ffunctions\u002Fthis-keyword",{"subtopic":153,"path":21,"order":20},{"subtopic":168,"path":169,"order":170},"Function Types & Parameters","\u002Fjavascript\u002Ffunctions\u002Ffunction-types-parameters",4,{"subtopic":172,"path":173,"order":174},"Generators & Iterators","\u002Fjavascript\u002Ffunctions\u002Fgenerators-iterators",5,{"path":176,"title":177},"\u002Fblog\u002Fjavascript-higher-order-functions","JavaScript Higher-Order Functions — Currying, Composition, Memoization and More",1781808676148]