# 实现compose的五种思路

• 面向过程
• 函数组合（reduce）
• 函数交织（AOP编程）
• Promise（sequence）
• Generator（yield）

## 什么是compose

``let tasks = [step1, step2, step3, step4]``

`compose`在函数式编程中是一个很重要的工具函数，在这里实现的`compose`有三点说明

• 第一个函数是多元的（接受多个参数），后面的函数都是单元的（接受一个参数）
• 执行顺序的自右向左的
• 所有函数的执行都是同步的（异步的后面文章会讲到）

``````let init = (...args) => args.reduce((ele1, ele2) => ele1 + ele2, 0)
let step2 = (val) => val + 2
let step3 = (val) => val + 3
let step4 = (val) => val + 4``````

``````steps = [step4, step3, step2, init]
``````

``````let composeFunc = compose(...steps)

console.log(composeFunc(1, 2, 3))``````

6 -> 6 + 2 = 8 -> 8 + 3 = 11 -> 11 + 4 = 15

## 实现compose的五种思路

### 面向过程

``````const compose = function(...args) {
let length = args.length
let count = length - 1
let result
return function f1 (...arg1) {
result = args[count].apply(this, arg1)
if (count <= 0) {
count = length - 1
return result
}
count--
return f1.call(null, result)
}
}``````

### 函数组合

``````f1 = (...arg) => step2.call(null, init.apply(null, arg))
f2 = (...arg) => step3.call(null, f1.apply(null, arg))
f3 = (...arg) => step4.call(null, f2.apply(null, arg))``````

`compose`实现

``````const _pipe = (f, g) => (...arg) => g.call(null, f.apply(null, arg))
const compose = (...args) => args.reverse().reduce(_pipe, args.shift())``````

### 函数交织（AOP）

``````Function.prototype.before = function(fn) {
const self = this
return function(...args) {
let result = fn.apply(null, args)
return self.call(null, result)
}
}

Function.prototype.after = function(fn) {
const self = this
return function(...args) {
let result = self.apply(null, args)
return fn.call(null, result)
}
}``````

`compose`实现

``````const compose = function(...args) {
let before = args.pop()
let start = args.pop()
if (args.length) {
return args.reduce(function(f1, f2) {
return f1.after(f2)
}, start.before(before))
}
return start.before(before)
}``````

``````step2.before(init).after(step3).after(step4)
fn3.after(step4)
fn3 = fn2.after(step3)
fn2 = fn1.before(step1)
fn1 = init -> step2 -> step3 -> step4``````

### Promise

`ES6`引入了`Promise``Promise`可以指定一个`sequence`，来规定一个执行`then`的过程，`then`函数会等到执行完成后，再执行下一个`then`的处理。启动`sequence`可以使用
`Promise.resolve()`这个函数。构建`sequence`可以使用`reduce`
`compose`实现

``````const compose = function(...args) {
let init = args.pop()
return function(...arg) {
return args.reverse().reduce(function(sequence, func) {
return sequence.then(function(result) {
return func.call(null, result)
})
}, Promise.resolve(init.apply(null, arg)))
}
}``````

### Generator

`Generator`主要使用`yield`来构建协程，采用中断，处理，再中断的流程。可以事先规定好协程的执行顺序，然后再下次处理的时候进行参数（结果）交接，有一点要注意的是，由于执行的第一个`next`是不能传递参数的，所以第一个函数的执行需要手动调用，再空耗一个`next`，后面的就可以同步执行了。
`generator`构建

``````
function* iterateSteps(steps) {
let n
for (let i = 0; i < steps.length; i++) {
if (n) {
n = yield steps[i].call(null, n)
} else {
n = yield
}
}
}
``````

`compose`实现

``````const compose = function(...steps) {
let g = iterateSteps(steps)
return function(...args) {
let val = steps.pop().apply(null, args)
// 这里是第一个值
console.log(val)
// 因为无法传参数 所以无所谓执行 就是空耗一个yield
g.next()
return steps.reverse.reduce((val, val1) => g.next(val).value, val)
}
}``````

## 总结

github地址

`github`里面针对每一种实现包含了完成的`demo`案例，就在`test.js`里面，以上就是实现同步`compose`的五种思路，每一种思路的出发点都不一样，但是实现的目的都是一样的，可以看出`javascript`是非常灵活的,借助`es6``Promise``Generator`也可以很优雅的实现。后面会介绍`compose`的异步实现，在函数式编程来看，异步的本质应该就是`Monad`

#### 你可能感兴趣的

8 条评论

+1 回复

0

dongzhe3917875 作者 · 2018年03月28日
vbyzc · 2018年10月26日

0

jackie198512 · 2月22日
0

jackie198512 · 2月22日
vbyzc · 2018年10月26日

0

before/after 也可以不挂在原型链上

jackie198512 · 2月22日