好久沒有更新了,最近學(xué)習(xí)的過程中一直在用聯(lián)想的思維來去看問題,javascript是一門非常靈活的語言,集合了好多語言的特性和多種編程模式,對于compose的實(shí)現(xiàn),就有非常多的思路,每一種思路都有自己的特點(diǎn),實(shí)現(xiàn)之后,有種殊途同歸的快感。下面就是我總結(jié)的實(shí)現(xiàn)compose函數(shù)的五種思路。
面向過程
函數(shù)組合(reduce)
函數(shù)交織(AOP編程)
Promise(sequence)
Generator(yield)
什么是compose簡單回顧一下compose,compose就是執(zhí)行一系列的任務(wù)(函數(shù)),比如有以下任務(wù)隊(duì)列,
let tasks = [step1, step2, step3, step4]
每一個(gè)step都是一個(gè)步驟,按照步驟一步一步的執(zhí)行到結(jié)尾,這就是一個(gè)compose
compose在函數(shù)式編程中是一個(gè)很重要的工具函數(shù),在這里實(shí)現(xiàn)的compose有三點(diǎn)說明
第一個(gè)函數(shù)是多元的(接受多個(gè)參數(shù)),后面的函數(shù)都是單元的(接受一個(gè)參數(shù))
執(zhí)行順序的自右向左的
所有函數(shù)的執(zhí)行都是同步的(異步的后面文章會(huì)講到)
還是用一個(gè)例子來說,比如有以下幾個(gè)函數(shù)
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
這幾個(gè)函數(shù)組成一個(gè)任務(wù)隊(duì)列
steps = [step4, step3, step2, init]
使用compose組合這個(gè)隊(duì)列并執(zhí)行
let composeFunc = compose(...steps) console.log(composeFunc(1, 2, 3))
執(zhí)行過程
6 -> 6 + 2 = 8 -> 8 + 3 = 11 -> 11 + 4 = 15
所以流程就是從init自右到左依次執(zhí)行,下一個(gè)任務(wù)的參數(shù)是上一個(gè)任務(wù)的返回結(jié)果,并且任務(wù)都是同步的,這樣就能保證任務(wù)可以按照有序的方向和有序的時(shí)間執(zhí)行。
所有思路的執(zhí)行過程都是上面的例子,以下只講compose實(shí)現(xiàn)
面向過程這個(gè)思路就是使用遞歸的過程思想,不斷的檢測隊(duì)列中是否還有任務(wù),如果有任務(wù)就執(zhí)行,并把執(zhí)行結(jié)果往后傳遞,這里是一個(gè)局部的思維,無法預(yù)知任務(wù)何時(shí)結(jié)束。直觀上最容易結(jié)束和理解。
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) } }
代碼地址
函數(shù)組合這個(gè)思路是一種函數(shù)組合的思想,將函數(shù)兩兩組合,不斷的生成新的函數(shù),生成的新函數(shù)挾裹了函數(shù)執(zhí)行的邏輯信息,然后再兩兩組合,不斷的傳遞下去,這種思路可以提前遍歷所有任務(wù),將任務(wù)組合成一個(gè)可以展開的組合結(jié)構(gòu),最后執(zhí)行的時(shí)候就像推導(dǎo)多米諾骨牌一樣。
函數(shù)的組合過程
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實(shí)現(xiàn)
const _pipe = (f, g) => (...arg) => g.call(null, f.apply(null, arg)) const compose = (...args) => args.reverse().reduce(_pipe, args.shift())
代碼地址
函數(shù)交織(AOP)這個(gè)實(shí)現(xiàn)的靈感來自javascript設(shè)計(jì)模式中的高階函數(shù),因?yàn)?b>compose的任務(wù)在本質(zhì)上就是函數(shù)執(zhí)行,再加上順序,所以可以把實(shí)現(xiàn)順序執(zhí)行放到函數(shù)本身,對函數(shù)的原型進(jìn)行方法的綁定。方法的作用對象是函數(shù),面向?qū)ο蠓庋b的數(shù)據(jù),面向函數(shù)封裝的是函數(shù)的行為。
需要對函數(shù)綁定兩個(gè)行為 before 和 after,before執(zhí)行函數(shù)多元部分(啟動(dòng)),after執(zhí)行函數(shù)單元部分
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) } }
這里對函數(shù)進(jìn)行方法的綁定,返回的是帶著函數(shù)執(zhí)行的規(guī)則的另外一個(gè)函數(shù),在這里是次序的排列規(guī)則,對返回的函數(shù)依然可以進(jìn)行鏈?zhǔn)秸{(diào)用。
compose實(shí)現(xiàn)
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) }
函數(shù)執(zhí)行過程
step2.before(init).after(step3).after(step4) fn3.after(step4) fn3 = fn2.after(step3) fn2 = fn1.before(step1) fn1 = init -> step2 -> step3 -> step4
代碼地址
PromiseES6引入了Promise,Promise可以指定一個(gè)sequence,來規(guī)定一個(gè)執(zhí)行then的過程,then函數(shù)會(huì)等到執(zhí)行完成后,再執(zhí)行下一個(gè)then的處理。啟動(dòng)sequence可以使用
Promise.resolve()這個(gè)函數(shù)。構(gòu)建sequence可以使用reduce
compose實(shí)現(xiàn)
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))) } }
代碼地址
GeneratorGenerator主要使用yield來構(gòu)建協(xié)程,采用中斷,處理,再中斷的流程??梢允孪纫?guī)定好協(xié)程的執(zhí)行順序,然后再下次處理的時(shí)候進(jìn)行參數(shù)(結(jié)果)交接,有一點(diǎn)要注意的是,由于執(zhí)行的第一個(gè)next是不能傳遞參數(shù)的,所以第一個(gè)函數(shù)的執(zhí)行需要手動(dòng)調(diào)用,再空耗一個(gè)next,后面的就可以同步執(zhí)行了。
generator構(gòu)建
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實(shí)現(xiàn)
const compose = function(...steps) { let g = iterateSteps(steps) return function(...args) { let val = steps.pop().apply(null, args) // 這里是第一個(gè)值 console.log(val) // 因?yàn)闊o法傳參數(shù) 所以無所謂執(zhí)行 就是空耗一個(gè)yield g.next() return steps.reverse.reduce((val, val1) => g.next(val).value, val) } }
代碼地址
總結(jié)github地址
github里面針對每一種實(shí)現(xiàn)包含了完成的demo案例,就在test.js里面,以上就是實(shí)現(xiàn)同步compose的五種思路,每一種思路的出發(fā)點(diǎn)都不一樣,但是實(shí)現(xiàn)的目的都是一樣的,可以看出javascript是非常靈活的,借助es6的Promise和Generator也可以很優(yōu)雅的實(shí)現(xiàn)。后面會(huì)介紹compose的異步實(shí)現(xiàn),在函數(shù)式編程來看,異步的本質(zhì)應(yīng)該就是Monad。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/88856.html
摘要:語言行為及特征狀態(tài)看不懂任何英語技術(shù),英語文檔,凡事沒有培訓(xùn)部在搞的,只有英文文檔的東西國內(nèi)一律沒大公司在用,都非主流,排斥英文文檔和新技術(shù),以及各種超出他學(xué)習(xí)能力范圍的技術(shù)。 在撰寫此文前首先必須申明的是本人不鄙視任何一種框架,也無意于挑起PHP框架間的戰(zhàn)爭,更沒有貶低某個(gè)框架使用者的用意,本文純粹個(gè)人的看法。你可以認(rèn)為我無知也好,或者裝逼也好,請不要試著在任何情況下,隨便發(fā)起言語的...
摘要:以下是在自己實(shí)習(xí)生面試的時(shí)候遇到的一個(gè)問題,事后自己也去總結(jié)了一下。 以下是在自己實(shí)習(xí)生面試的時(shí)候遇到的一個(gè)問題,事后自己也去總結(jié)了一下。問題描述如下:一個(gè)外層div里面嵌套兩個(gè)內(nèi)部div,外層div高度固定(假設(shè)未知),內(nèi)層上面的div高度固定,如何讓下面的div實(shí)現(xiàn)撐滿外層的div高度?看到過網(wǎng)上有類似的問題,但是大部分都是假設(shè)外層高度為100%或者是已知的,而我遇到的是外層高度雖...
摘要:實(shí)現(xiàn)垂直居中的幾種方法分場景介紹包裹行內(nèi)元素效果圖包裹行內(nèi)塊級元素效果圖結(jié)構(gòu)效果圖注容器若設(shè)置了再設(shè)置的無效,即會(huì)自動(dòng)填滿的高寬若未設(shè)置,則自適應(yīng)的實(shí)際寬高 實(shí)現(xiàn)垂直居中的幾種方法(分場景介紹) line-height (包裹行內(nèi)元素) 123456788 12345555 .wrap { height: 100px; line-height:...
摘要:二內(nèi)邊距法另一種方法和行高法很相似,它同樣適合一行或幾行文字垂直居中,原理就是利用將內(nèi)容垂直居中,比如這段代碼的效果和法差不多。 一、行高(line-height)法 如果要垂直居中的只有一行或幾個(gè)文字,那它的制作最為簡單,只要讓文字的行高和容器的高度相同即可,比如: p { height:30px; line-height:30px; width:100px; overflow:hi...
閱讀 3109·2021-09-22 15:54
閱讀 3997·2021-09-09 11:34
閱讀 1780·2019-08-30 12:48
閱讀 1171·2019-08-30 11:18
閱讀 3441·2019-08-26 11:48
閱讀 927·2019-08-23 17:50
閱讀 2126·2019-08-23 17:17
閱讀 1252·2019-08-23 17:12