很久没有更新了,最近学习的过程当中一直在用联想的思惟来去看问题,javascript
是一门很是灵活的语言,集合了好多语言的特性和多种编程模式,对于compose
的实现,就有很是多的思路,每一种思路都有本身的特色,实现以后,有种异曲同工的快感。下面就是我总结的实现compose
函数的五种思路。javascript
简单回顾一下compose
,compose
就是执行一系列的任务(函数),好比有如下任务队列,java
let tasks = [step1, step2, step3, step4]
每个step
都是一个步骤,按照步骤一步一步的执行到结尾,这就是一个compose
compose
在函数式编程中是一个很重要的工具函数,在这里实现的compose
有三点说明git
仍是用一个例子来讲,好比有如下几个函数es6
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
这几个函数组成一个任务队列github
steps = [step4, step3, step2, init]
使用compose
组合这个队列并执行编程
let composeFunc = compose(...steps) console.log(composeFunc(1, 2, 3))
执行过程
6 -> 6 + 2 = 8 -> 8 + 3 = 11 -> 11 + 4 = 15
因此流程就是从init
自右到左依次执行,下一个任务的参数是上一个任务的返回结果,而且任务都是同步的,这样就能保证任务能够按照有序的方向和有序的时间执行。设计模式
全部思路的执行过程都是上面的例子,如下只讲compose
实现数组
这个思路就是使用递归的过程思想,不断的检测队列中是否还有任务,若是有任务就执行,并把执行结果日后传递,这里是一个局部的思惟,没法预知任务什么时候结束。直观上最容易结束和理解。promise
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) } }
代码地址app
这个思路是一种函数组合的思想,将函数两两组合,不断的生成新的函数,生成的新函数挟裹了函数执行的逻辑信息,而后再两两组合,不断的传递下去,这种思路能够提早遍历全部任务,将任务组合成一个能够展开的组合结构,最后执行的时候就像推导多米诺骨牌同样。
函数的组合过程
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())
这个实现的灵感来自javascript
设计模式中的高阶函数,由于compose
的任务在本质上就是函数执行,再加上顺序,因此能够把实现顺序执行放到函数自己,对函数的原型进行方法的绑定。方法的做用对象是函数,面向对象封装的数据,面向函数封装的是函数的行为。
须要对函数绑定两个行为 before
和 after
,before
执行函数多元部分(启动),after
执行函数单元部分
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
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
主要使用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
里面针对每一种实现包含了完成的demo
案例,就在test.js
里面,以上就是实现同步compose
的五种思路,每一种思路的出发点都不同,可是实现的目的都是同样的,能够看出javascript
是很是灵活的,借助es6
的Promise
和Generator
也能够很优雅的实现。后面会介绍compose
的异步实现,在函数式编程来看,异步的本质应该就是Monad
。