#私藏项目实操分享# 从【if…else…】到【责任链】再到【composeAOP】,顺带把【传参】解决了

title: 从【if...else...】到【责任链】再到【composeAOP】,顺带把【传参】解决了~date: 2021-08-15 tags: - JS categories: 2021 嵌套毛毛虫 我猜你一定见过这样的代码: if(condition1 === A1){ if(condition2 === A2){ ... }else if(condition2 === B2){ ... }else if(condition2 === C2){ ... }else{ ... } }esle if(condition1 === B1){ ... ... ... }else if(condition1 === C1){ ... ... ... }else if(condition1 === D1){ ... ... ... }else{ ... } 讲真,并不是说这个代码有多坏,但是每次看到的时候都会引起本瓜不适。感觉它就像是一只毛毛虫。。。为了形象的表达这一点,本瓜诚邀灵魂画师 ​​ ​ ​​守护安东尼​​ 作示意图一张,salute!!( ̄︶ ̄)↗  * 图片来源:守护安东尼,未经允许,随意转载。 这样写,总是会伴随着各种各样的逻辑判断、隐式输入、输出,还真不太敢动它,担心它直接“死”给你看! 责任链竹节 镜头转向【责任链】,它是 23 种设计模式之一,属于行为型模式,关注对象之间的交互、通信;参数输入到一个初始函数中,如果不满足当前函数条件,则传递到下一函数中进行处理,满足停止,不满足再传递,这样 one by one 向后进行,直至满足条件或传递结束。 一个个元函数就像是一节节竹节,独立可拆卸、再任意组装; 闲话少说,实现它的代码大致是这样的: function A1(condition1){ chainA2.next(chainB2).next(chainC2); return condition1 === A1 ? chainA2.setParam(condition2) : 'doNext' } function B1(condition1){ } function C1(condition1){ } function D1(condition1){ } ... function A2(condition2){ } function B2(condition2){ } function C2(condition2){ } chainA1.next(chainB1).next(chainC1).next(chainD1) chainA1.setParam(condition1) 整体感官上,是不是像竹子一样?每一节(函数输入、输出)特别清晰。关键是,它解耦了,组装起来也超级方便~ * 图片来源:守护安东尼,未经允许,随意转载。 核心的,生成 Chain 的代码如下:Chain 函数是高级函数,入参是一个函数。这里通过原型链的方式给它加了 next、setParam 两个属性。next 的入参也是 fn,用于设置下一个处理函数,setParam 用于传递原始入参; var Chain = function( fn ){ this.fn = fn; this.successor = null; }; Chain.prototype.next = function( successor ){ return this.successor = successor; }; Chain.prototype.setParam = function(){ var ret = this.fn.apply( this, arguments ); if ( ret === 'doNext' ){ return this.successor && this.successor.setParam.apply( this.successor, arguments ); } return ret; }; 函数特性AOP 实际上,利用 JavaScript 的函数式特性,还有一种更加方便的方法来创建责任链 —— 即 AOP。面向切面编程(AOP:Aspect Oriented Program)思想的简单理解: 动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。 代码如下: /** * 函数交织(AOP) * @param {*} fn * @returns */ Function.prototype.before = function(fn) { const self = this return function(...args) { const result = fn.apply(null, args) return self.call(null, result) } } Function.prototype.after = function(fn) { const self = this return function(...args) { const result = self.apply(null, args) return fn.call(null, result) } } 调用示例: fn1 = step2.before(init).after(step3).after(step4) //fn1 = init -> step2 -> step3 -> step4 我们可以任意指定、搭配函数的执行先后关系; ?composeAOP 还记得​​《感谢 compose 函数,让我的代码屎山逐渐美丽了起来》​​这篇文章吗?compose 其实有很多种写法!我们可以借助上面的 before 和 after 函数实现这一版的 composeAOP const composeAOP = function(...args) { const before = args.pop() const start = args.pop() if (args.length) { return args.reduce(function(f1, f2) { return f1.after(f2) }, start.before(before)) } return start.before(before) } 对了,回答之前有不少人问为啥 compose 是从右至左执行?? const compose = function(...args) { if (args.length) { return args.reverse().reduce(function(f1, f2) { return f1.after(f2) }) } } compose(step4,step3,step2,step1,init)("start") 这里说一下:原因是它模拟了通常情况下函数逐层调用,层层包裹的顺序,像剥洋葱一样,从外而内,从右至左去解析: step4(step3(step2(step1(init(...args))))) // 一层层括号像极了洋葱皮 如果你喜欢从左至右,或换 ​​pop()​​​ 为 ​​shift()​​​ ,或去掉那层 ​​reverse()​​​ 即可,或改 ​​after​​​ 为 ​​before​​......顺序问题,无关好坏,全凭喜好~ 传参问题!! 如果你有心在控制台试试以上代码,不难发现其中的一个很严重的传参问题!!这个问题在《compose 优化屎山》那篇文章实际上也存在,也有细心的掘友反馈。 function init(...args){ console.log(args) return [...args,"init"] } function step1(...args){ console.log(args) return [...args,"step1"] } function step2(...args){ console.log(args) return [...args,"step2"] } function step3(...args){ console.log(args) return [...args,"step3"] } compose(step3,step2,step1,init)("start") 随着参数的传递,args 数组的维度在不断上升。 如果我们使用 ​​flat(Infinity)​​ 拉平数组,传参就变成了这样: 这样做有一个很大的问题就是:需要对照数组的传参顺序!这是很头疼的,因为保不定哪天就要增删改流程参数。所以,期望是能换成对象作传参, 消除按顺序传参的桎梏。比如: {start:"start",init:"init",step1:"step1"......} 直接动手试试: function init(...args){ console.log(JSON.stringify(args)) return {args:args,init:"init"} } function step1(...args){ console.log(JSON.stringify(args)) return {args:args,step1:"step1"} } function step2(...args){ console.log(JSON.stringify(args)) return {args:args,step2:"step2"} } function step3(...args){ console.log(JSON.stringify(args)) return {args:args,step3:"step3"} } compose(step3,step2,step1,init)("start") 得到: 显然这不是我们想要的,我们得再不断打印寻找规律: 哇噢~在 ​​​ ​​step3​​​ 中想获取 ​​step1​​​,就要 2 个 ​​.args[0]​​; 在 ​​step2​​​ 中想获取 ​​step1​​​,只要 1 个 ​​.args[0]​​; 我们基本可以推出:想获得前 N 步的参数,只需带 N 个 ​​.args[0]​​​于是乎,我们可以尝试写一个 ​​​​​ ​​getCountStepAttr()​​ 函数,用于在某个函数步骤中,获得前第 N 步的入参,通过调用对象属性的方式!来吧,展翅~ function getCountStepAttr(args,N){ // 需要前第几(N)步的参数 N = N -1 let resObj = args[0] for(let i =0;i<N;i++){ resObj = resObj.args[0] } return resObj } 直接就可以测试使用了: 完整代码 贴下完整代码,你可以拷贝在控制台玩一玩看看,本瓜相信你一定会有所收获!! Function.prototype.after = function(fn) { const self = this return function(...args) { let result = self.apply(null, args) return fn.call(null,result) } } const compose = function(...args) { if (args.length) { return args.reverse().reduce(function(f1, f2) { return f1.after(f2) }) } } const getCountStepAttr = function(args,N){ // 获取前 N 步的入参; N = N -1 let resObj = args[0] for(let i =0;i<N;i++){ resObj = resObj.args[0] } return resObj } function init(...args){ console.log("【在 init 中调用原始传参】:",getCountStepAttr(args,1)) return {args:args,init1:"init1",init:"init"} } function step1(...args){ return {args:args,step1:"step1"} } function step2(...args){ return {args:args,step2:"param-step2",step2Add:"param-step2-add"} } function step3(...args){ console.log("【在 step3 中调用 step2 的传参】:",getCountStepAttr(args,1).step2 , getCountStepAttr(args,1).step2Add) console.log("【在 step3 中调用 init 的传参】:",getCountStepAttr(args,3).init , getCountStepAttr(args,3).init1) console.log("【在 step3 中调用原始传参】:",getCountStepAttr(args,4)) return {args:args,step3:"step3"} } compose(step3,step2,step1,init)("start") 小结展望 本篇在讲什么?其实还是那金光闪闪的五个大字: 函数式编程。我们将过程中的命令式代码用一个个简单的纯函数进行封装,最后组合成各种丰富的功能。 你可以在这个过程中,或任意拆卸、或增添补充、或重构设计,真的不用太担心隐藏的逻辑错漏或耦合造成的复杂业务难梳理! 我们用函数的输入、输出表达映射关系,用函数名表达函数内的功能实现,用参数的传递表达业务逻辑,用封闭的作用域环境构造干净的代码~ 当然,你或许还有很多好的想法, 代码的干净之路 还有很长一段要走!高山仰止,景行行止,虽不能至,心向往之。再说,能不能“至”还真不一定呢!都看到这里,不如点个赞吧 撰文不易,多谢鼓励 欢迎点赞、收藏、评论~ 我是安东尼,一个有选择、不盲目的技术分享者,再会~~ 未经允许,禁止转载,授权联系 anthony1453

提供全面的网站源码学习交流,小程序、APP、H5、支付、游戏、区块链、商城、直播、影音、小说、公众号等源码学习交流。
精品源码资源网 » #私藏项目实操分享# 从【if…else…】到【责任链】再到【composeAOP】,顺带把【传参】解决了
赞助VIP 享更多特权,立即登录下载海量资源
喜欢我嘛?喜欢就按“ctrl+D”收藏我吧!♡