标准答案
- 高阶函数是接收函数作为参数,或返回一个函数的函数。map、filter、reduce、debounce、throttle、once、memoize 都是常见例子。
- 它的价值是把可变的业务逻辑作为函数传入,把固定流程封装起来。例如数组遍历流程固定,具体映射或筛选规则由回调决定。
- compose 和 pipe 都是函数组合。compose 通常从右到左执行,pipe 通常从左到右执行,用来把多个单步处理串成处理管道。
- 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,忽略对象键顺序和不可序列化值。