compose是函数式编程中使用较多的一种写法, 它把逻辑解耦在各个函数中,通过compose的方式组合函数, 将外部数据依次通过各个函数的加工,生成结果。
最简单例子
f(x) = (5 + x) ^3
这个运算有两个操作,先做加法运算,再做幂运算
function power(x) {
return Math.pow(x, 10)
}
function add(x) {
return x + 5
}
function compose(fn1, fn2) {
return function(x) {
return fn1(fn2(x))
}
}
// 可简写成
const compose = (fn1, fn2) => x => fn1(fn2(x))
compose(power, add)(5) // 1000
这是两个函数compose的实现方式
如果有N个函数呢,难道每次要写成
const compose = (fn1, ..., fnn) => x => fn1(...(fnn(x)))
如果N是100呢?
我们仔细思考一下,把N个输入收敛成一个结果,是不是很像js的某个API?就是Array.prototype.reduce方法
这个方法会对数组中的每个元素执行一个由你提供的reducer函数(升序执行),将其结果汇总为单个返回值。
那么给定一个[fn1, fn2, ..., fnn]的数组funcs,如何实现函数组合的形式呢?
根据reduce的定义,似乎只要实现reducer方法就行
const reducer = (accumulator, currentvalue) => {
return (x) => {
accumulator(currentvalue(x))
}
}
// 简写
const reducer = (acc, cur) => x => acc(cur(x))
const compose = func.reduce(reducer)
让我们做一个验证
callback | accumulator | currentvalue | 返回值 |
---|---|---|---|
round1 | fn1 | fn2 | x => fn1(fn2(x)) |
round2 | x => fn1(fn2(x)) | fn3 | x =>((x) => fn1(fn2(x)))(fn3(x)) |
因为reduce函数没有初始值,所以第一轮accumular将会是fn1, currrentvalue将会是fn2(见MDN中有无初始值时reduce的行为)。在第二轮中,累计值accumulator将会变成上一轮的返回值,执行reducer回调之后,会生成x =>((x) => fn1(fn2(x)))(fn3(x))。
这时候令我们费解的是,如果只有funcs = [fn1, fn2, fn3],这时候实际上reduce已经结束执行了,它的返回值怎么一点也不像我们期待的x => fn1(fn2(fn3(x))) 这种形式?
实际上表格中第二轮返回值里加粗部分是一个立即执行函数,而fn3(x)则是这个函数的参数,代入之后即可得x => fn1(fn2(fn3(x)))
完整的表格应该是
callback | accumulator | currentvalue | 返回值 |
---|---|---|---|
round1 | fn1 | fn2 | x => fn1(fn2(x)) |
round2 | x => fn1(fn2(x)) | fn3 | x => fn1(fn2(fn3(x))) |
round3 | x => fn1(fn2(fn3(x))) | fn4 | x => fn1(fn2(fn3(fn4(x)))) |
... | |||
round n | x=> fn1(fn2(...fn(n-1)(x))) | fnn | x=> fn1(fn2(...fnn(x))) |
这个方法就是redux 中间件中实现多个函数组合的方式,
非常的简洁优美, 然而了损失一定的可读性,需要积累一些函数式编程的基础才能快速参透。
网友评论