前端中的中间件

场景

function stepOne(msg) {
    console.log(msg)
}

function checkStepOne(msg) {
    console.log(`check:${msg}`)
    return msg === 'success' ? true : false
}

现有函数 stepOne(),请求在不改写函数的基础上,在实行该函数之前增加搜检 checkStepOne(),

搜检返回 ture,再实行 stepOne()

我们大都会如许写

function flow(msg){
    if(checkStepOne(msg)){
        return stepOne(msg)
    }
    return false
}

很明显,如许的 flow() 很不天真

假如如今又有 stepTwo(),一样需要在实行之前进行搜检 checkStepTwo(),再写一个flowTwo() 吗?

不,修正函数 flow()

function flow(fn, checkFn, msg) {
    if (checkFn(msg)) {
        return fn(msg)
    }
    return false
}

flow(stepOne, checkStepOne, 'success')
flow(stepTwo, checkStepTwo, 'success')

滑水的日子木有几天,又涌现了新的需求,在 checkStepOne() 之前,另有一步操纵,beforeCheckStepOne()

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,就不继承下面的步骤了

套路呢?无妨多一些

AOP,Aspect-oriented programming,面向切面编程

改写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')

Middle

《前端中的中间件》

盗图自前端开辟中的中间件

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])()

瞅瞅 redux中compose.js 是怎样写的

/**
 * 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 是怎样玩的

如它诠释中所说,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

高兴就好

Compose & middle

回到之前的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')  
}

高仿express中的use

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) 在一些场所,功用雷同

参考资料

    原文作者:nbb3210
    原文地址: https://segmentfault.com/a/1190000013944375
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞