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) 在一些场合,功能相同