function stepOne(msg) { console.log(msg) } function checkStepOne(msg) { console.log(`check:${msg}`) return msg === 'success' ? true : false }
現有函數 stepOne(),要求在不改寫函數的基礎上,在執行該函數以前添加檢查 checkStepOne(),html
檢查返回 ture,再執行 stepOne()前端
咱們大都會這樣寫node
function flow(msg){ if(checkStepOne(msg)){ return stepOne(msg) } return false }
很明顯,這樣的 flow() 很不靈活react
若是如今又有 stepTwo(),一樣須要在執行以前進行檢查 checkStepTwo(),再寫一個flowTwo() 嗎?git
不,修改函數 flow()github
function flow(fn, checkFn, msg) { if (checkFn(msg)) { return fn(msg) } return false } flow(stepOne, checkStepOne, 'success') flow(stepTwo, checkStepTwo, 'success')
滑水的日子木有幾天,又出現了新的需求,在 checkStepOne() 以前,還有一步操做,beforeCheckStepOne()express
function beforeCheckStepOne(msg) { console.log(`beforeCheckStepOne is '${msg}'`) }
修改函數 flow()編程
function flow(fns, msg) { let current = fns.shift() let result while (current) { result = current(msg) if (result === false) { return false } current = fns.shift() } return result } flow([beforeCheckStepOne, checkStepOne, stepOne], 'fail') // beforeCheckStepOne is 'fail' // checkMsg is 'fail'
flow(fns, msg) 中 fns 用來存儲要執行的步驟,若是上一個步驟返回 false,就不繼續下面的步驟了redux
AOP,Aspect-oriented programming,面向切面編程app
改寫Function的原型
Function.prototype.before = function (fn) { let rawFn = this return function () { if (fn.apply(null, arguments) === false) { return false } rawFn.apply(null, arguments) } } stepOne.before(checkStepOne).before(beforeCheckStepOne)('success') // beforeCheckStepOne is 'success' // checkMsg is 'success' // success
再換個花樣
Function.prototype.after = function (fn) { let rawFn = this return function () { if (rawFn.apply(null, arguments) === false) { return false } fn.apply(null, arguments) } } beforeCheckStepOne.after(checkStepOne).after(stepOne)('success') // beforeCheckStepOne is 'success' // checkMsg is 'success' // success
OS:這樣寫不會被人打嗎?不只改寫了 Function.prototype,看起來還太裝逼
滑水的日子木有幾天,又出現了新的需求,步驟之間能傳遞額外的消息
改造完,以下,多個 context 對象,用於傳遞信息
function stepOne(msg, context) { console.log(msg) console.log(context.data) } function checkStepOne(msg, context) { console.log(`checkMsg is '${msg}'`) return msg === 'success' ? true : false } function beforeCheckStepOne(msg, context) { console.log(`beforeCheckStepOne is '${msg}'`) context.data = 'from beforeCheckStepOne' } function flow(fns, msg) { let currentFn = fns.shift() let result let context = {} while (currentFn) { result = currentFn(msg, context) if (result === false) { return false } currentFn = fns.shift() } return result } flow([beforeCheckStepOne, checkStepOne, stepOne], 'success')
盜圖自前端開發中的中間件
function middle1(next) { return () => { console.log('Enter the middle1') next() console.log('Exit the middle1') } } function middle2(next) { return () => { console.log('Enter the middle2') next() console.log('Exit the middle2') } } function middle3(next) { return () => { console.log('Enter the middle3') next() console.log('Exit the middle3') } } function next() { console.log('next') } middle1(middle2(middle3(next)))()
這仍是3箇中間件,調用起來就如此醜陋了,當有更多的中間件該是如何
重寫個flow()函數好了
function flow(funcs, rawNext) { let next = funcs.pop() next = next(rawNext) let middle while (funcs.length > 0) { middle = funcs.pop() next = middle(next) } return next } flow([middle1, middle2, middle3], next)() // Enter the middle1 // Enter the middle2 // Enter the middle3 // next // Exit the middle3 // Exit the middle2 // Exit the middle1
執行 flow() 的過程,就是在拼湊 middle1(middle2(middle3(next)))
的過程
同時,next() 也能夠當作是個中間件
function flow(funcs) { let next = funcs.pop() while (funcs.length > 0) { let middle = funcs.pop() next = middle(next) } return next } flow([middle1, middle2, middle3, next])()
沒有定義過多變量的 while,老是能夠用 reduceRight 修飾一下
function flow(funcs) { return funcs.reduceRight((a, b) => b(a)) } flow([middle1, middle2, middle3, next])()
/** * Composes single-argument functions from right to left. The rightmost * function can take multiple arguments as it provides the signature for * the resulting composite function. * * @param {...Function} funcs The functions to compose. * @returns {Function} A function obtained by composing the argument functions * from right to left. For example, compose(f, g, h) is identical to doing * (...args) => f(g(h(...args))). */ export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) }
如它註釋中所說,compose(f, g, h) is identical to doing (...args) => f(g(h(...args)))
// 輸入16進制字符串,返回8位2進制字符串 let sixTeenToTen = x => parseInt(x, 16) let tenToTwo = x => (x).toString(2) let addZero = x => ('00000000' + x).slice(-8) let sixTeenToTwo = compose(addZero, tenToTwo, sixTeenToTen) console.log(sixTeenToTwo('0x62')) // 01100010
固然,你也能夠這樣寫
let sixTeenToTwo2 = x => ('00000000' + (parseInt(x, 16)).toString(2)).slice(-8) console.log(sixTeenToTwo2('0x62')) // 01100010
開心就好
回到以前的middle1,middle2,middle3 函數那,同時把next改寫成middle4
function middle1(next) { return (a) => { console.log('Enter the middle1') next(a) console.log('Exit the middle1') } } function middle2(next) { return (a) => { console.log('Enter the middle2') next(a) console.log('Exit the middle2') } } function middle3(next) { return (a) => { console.log('Enter the middle3') next(a) console.log('Exit the middle3') } } function middle4(next) { return (a) => { console.log(`middle4:${a}`) } } let middles = compose(middle1, middle2, middle3, middle4)() middles('msg') // Enter the middle1 // Enter the middle2 // Enter the middle3 // middle4:msg // Exit the middle3 // Exit the middle2 // Exit the middle1
值得一提的是,let middles = compose(middle1, middle2, middle3, middle4)()
最後有一組()
,調用函數,至關於middle1(middle2(middle3(middle4())))
給 middle4 傳入空參數
執行 middle4(),返回
(a) => { console.log(`middle4:${a}`) }
這個函數,做爲 next 參數,執行 middle3(next),返回
(a) => { console.log('Enter the middle3') console.log(`middle4:${a}`) console.log('Exit the middle3') }
這個函數,做爲 next 參數,執行 middle2(next),返回
(a) => { console.log('Enter the middle2') console.log('Enter the middle3') console.log(`middle4:${a}`) console.log('Exit the middle3') console.log('Exit the middle2') }
這個函數,做爲 next 參數,執行 middle1(next),返回
(a) => { console.log('Enter the middle1') console.log('Enter the middle2') console.log('Enter the middle3') console.log(`middle4:${a}`) console.log('Exit the middle3') console.log('Exit the middle2') console.log('Exit the middle1') }
因此,最終 middles 就是這樣的
let middles = compose(middle1, middle2, middle3, middle4)() // 至關於 let middles = (a) => { console.log('Enter the middle1') console.log('Enter the middle2') console.log('Enter the middle3') console.log(`middle4:${a}`) console.log('Exit the middle3') console.log('Exit the middle2') console.log('Exit the middle1') }
class Middle { constructor() { this.funcs = [] } use(fn) { this.funcs.push(fn) return this } work() { this.funcs.reduceRight((fn1, fn2) => { return () => fn2(fn1) }, () => {})() } } function m1(next) { console.log('Enter the middle1') next() console.log('Exit the middle1') } function m2(next) { console.log('Enter the middle2') next() console.log('Exit the middle2') } function m3(next) { console.log('Enter the middle3') next() console.log('Exit the middle3') } function m4(next) { console.log('Enter the middle4') console.log('Exit the middle4') } let m = new Middle() m.use(m1) m.use(m2) m.use(m3) m.use(m4) m.work()
來段小插曲
let fns = [m1, m2, m3, m4, m5] fns.reduceRight((fn1, fn2) => () => fn2(fn1), () => {})() // 至關於 fns.reduceRight((fn1, fn2) => { return () => fn2(fn1) }, () => {})() // 結合以前定義的 m1, m2, m3, m4, m5, 獲得結果 // Enter the middle1 // Enter the middle2 // Enter the middle3 // Enter the middle4 // Exit the middle4 // Exit the middle3 // Exit the middle2 // Exit the middle1
其實那段 reduceRight,原本是寫成 while 的
let fns = [m1, m2, m3, m4, m5] let next = () => {} while(fns.length > 0){ let fn = fns.pop() next = () => fn(next) } next() // 一直輸出 Enter the middle1
因此作了些調整
let fns = [m1, m2, m3, m4, m5] let next = () => {} while (fns.length > 0) { let fn = fns.pop() next = function (fn, next) { return () => fn(next) }(fn, next) } next() // 輸出結果符合預期
來自網上的套路是這樣的
class Middle { constructor() { this.funcs = [] this.middlewares = [] } use(fn) { this.funcs.push(fn) return this } next(fn) { if (this.middlewares && this.middlewares.length > 0) { let ware = this.middlewares.shift() ware.call(this, this.next.bind(this)) } } work() { this.middlewares = this.funcs.map(f => f) this.next() } }
感受大概就是這個意思
m4 = m4.bind(null, m5) m3 = m3.bind(null, m4) m2 = m2.bind(null, m3) m1 = m1.bind(null, m2) m1() // 或者 m1.call(null, m2.bind(null, m3.bind(null, m4.bind(null, m5))))
再囉嗦地解釋下,由於我一開始是看半天沒能理解
let m = new Middle() m.use(m1) m.use(m2) m.use(m3) m.use(m4) m.use(m5) m.work()
執行 m.work() 後,
執行 m.next()
從 m.middlewares 中取出 m1
執行 m1.call(m, m.next)
執行 m1 函數體內
console.log('Enter the middle1')
而後遇到 next()
實際上執行了 m.next()
從 m.middlewares 中取出 m2
執行 m2.call(m, m.next)
執行 m2 函數體內
console.log('Enter the middle2')
而後遇到 next()
實際上執行了 m.next()
從 m.middlewares 中取出 m3
執行 m3.call(m, m.next)
執行 m3 函數體內
console.log('Enter the middle3')
...
直至結束
class Middle { constructor() { this.funcs = [] this.middlewares = [] this.options = null } use(fn) { this.funcs.push(fn) return this } next(fn) { if (this.middlewares && this.middlewares.length > 0) { let ware = this.middlewares.shift() ware.call(this, this.options, this.next.bind(this)) } } work(options) { this.middlewares = this.funcs.map(f => f) this.options = options this.next() } }
使用樣例
function m1(options, next) { console.log('Enter the middle1') console.log(options.name) next() console.log('Exit the middle1') } function m2(options, next) { options.name = 'm2' console.log('Enter the middle2') console.log(options.name) next() console.log('Exit the middle2') } function m3(options, next) { options.name = 'm3' console.log('Enter the middle3') console.log(options.name) next() console.log('Exit the middle3') } function m4(options, next) { console.log('Enter the middle4') console.log(options.name) console.log('Exit the middle4') } function m5(options, next) { console.log('Enter the middle5') next() console.log('Exit the middle5') } let m = new Middle() m.use(m1) m.use(m2) m.use(m3) m.use(m4) m.use(m5) m.work({ name: 'm' }) // Enter the middle1 // m // Enter the middle2 // m2 // Enter the middle3 // m3 // Enter the middle4 // Exit the middle4 // Exit the middle3 // Exit the middle2 // Exit the middle1
一樣功能的代碼
let fns = [m1, m2, m3, m4, m5] let next = () => {} let options = { name: 'm' } while (fns.length > 0) { let fn = fns.pop() next = function (fn, options, next) { return () => fn(options, next) }(fn, options, next) } next()
一樣功能的代碼
let options = { name: 'm' } m4 = m4.bind(null, options, m5) m3 = m3.bind(null, options, m4) m2 = m2.bind(null, options, m3) m1 = m1.bind(null, options, m2) m1() // 至關於 fns.reduceRight((fn1, fn2) => fn2.bind(null, options, fn1))()
一樣功能的代碼
let options = { name: 'm' } m44 = () => m4(options, m5) m33 = () => m3(options, m44) m22 = () => m2(options, m33) m11 = () => m1(options, m22) m11() // 至關於 fns.reduceRight((fn1, fn2) => { return () => fn2(options, fn1) }, () => {})() // 再精煉的話 fns.reduceRight((fn1, fn2) => () => fn2(options, fn1), () => {})() // 感受我3min之後就不認得本身寫的代碼了
fn.bind(null, args) 和 return () => fn(args) 在一些場合,功能相同