前端面试 P019:函数组合与同步数据管道
Showing the original in Chinese (China)
来源:方格前端讲面试 · 19. 函数组合、记忆化,22 分 04 秒。
回看定位:01:06 组合定义 · 03:51 方向 · 05:57 reduce 的原解释
compose 与 pipe 的方向
compose(f, g)(x) 表达 f(g(x)),从右向左执行;pipe(f, g)(x) 表达 g(f(x)),从左向右执行。前一步的结果应符合下一步的输入约定。
const compose = (...functions) => input =>
functions.reduceRight((value, fn) => fn(value), input);
const pipe = (...functions) => input =>
functions.reduce((value, fn) => fn(value), input);
const add1 = x => x + 1;
const multiply2 = x => x * 2;
const subtract3 = x => x - 3;
console.log(compose(subtract3, multiply2, add1)(5)); // 9
console.log(pipe(add1, multiply2, subtract3)(5)); // 9
console.log(compose()(5)); // 5
这里明确只演示单输入的同步管道。空组合返回输入,单函数组合等价于执行该函数。
需要纠正:reduce 默认不是从右到左
视频在解释 compose 时说 reduce 默认从右向左遍历,这不正确。Array.reduce 按升序索引遍历;reduceRight 才按降序。即使通过 reduce 构造出右向左执行的嵌套函数,也是包装函数的组合方式造成的,不是 reduce 本身倒着遍历。
错误与异步
同步某一步抛错,后续步骤不再执行,异常传播给调用者。若函数返回 Promise,上面的基础版本会把 Promise 对象直接传给下一步,不会自动 await。
需要异步管道时可用 Promise.resolve(input) 作为起点,再通过 then 串联步骤,并在边界捕获异常。不要把同步 compose 的结果直接当作异步执行器。
函数组合便于拆分职责,但链太长、类型跳变或隐含副作用太多时会降低可读性;关键中间结果值得命名与检查。
同集另一主题见 前端面试 P019:记忆化缓存与递归调用的纠错。