标准答案

  1. 高阶函数是接收函数作为参数,或返回一个函数的函数。map、filter、reduce、debounce、throttle、once、memoize 都是常见例子。
  2. 它的价值是把可变的业务逻辑作为函数传入,把固定流程封装起来。例如数组遍历流程固定,具体映射或筛选规则由回调决定。
  3. compose 和 pipe 都是函数组合。compose 通常从右到左执行,pipe 通常从左到右执行,用来把多个单步处理串成处理管道。
  4. memoize 是缓存函数调用结果,下一次相同输入直接返回缓存。它适合确定性纯函数和高成本计算,实现时要考虑缓存 key、参数序列化、缓存容量、this 和副作用。

题目解析

高阶函数不是越多越好。它适合抽象稳定流程,如果只是把简单代码包成多层回调,反而会降低可读性。

compose/pipe 要求前一个函数的输出能作为后一个函数的输入。组合链越长,越需要保持每一步的输入输出清晰。

memoize 的前提是相同输入应得到相同输出。对有副作用、依赖时间、依赖外部状态的函数做 memoize,容易得到过期或错误结果。

代码示例

下面示例展示 pipe、compose 和一个保留 this 的 memoize 核心实现。

JavaScript
const pipe = (...fns) => (input) => {
  return fns.reduce((value, fn) => fn(value), input)
}

const compose = (...fns) => (input) => {
  return fns.reduceRight((value, fn) => fn(value), input)
}

function memoize(fn, getKey = (...args) => JSON.stringify(args)) {
  const cache = new Map()

  return function memoized(...args) {
    const key = getKey(...args)

    if (cache.has(key)) {
      return cache.get(key)
    }

    const result = fn.apply(this, args)
    cache.set(key, result)
    return result
  }
}

const normalize = pipe(
  (value) => value.trim(),
  (value) => value.toLowerCase()
)

normalize('  JS  ') // 'js'

常见误区

  • 把所有接收回调的函数都写得过度抽象,降低可读性。
  • memoize 有副作用函数,导致缓存结果错误。
  • 缓存 key 直接 JSON.stringify,忽略对象键顺序和不可序列化值。

作者信息