很久沒有更新了,最近學習的過程當中一直在用聯想的思惟來去看問題,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
。