场景
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) 在一些场合,功能相同
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。