函数式编程,封装并组合,顺带把【传参】解决了
作者:掘金安东尼
🐛嵌套毛毛虫
我猜你一定见过这样的代码:
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){
return condition1 === B1 ? ... : 'doNext'
}
function C1(condition1){
return condition1 === C1 ? ... : 'doNext'
}
function D1(condition1){
return condition1 === D1 ? ... : 'doNext'
}
...
function A2(condition2){
return condition2 === A2 ? ... : 'doNext'
}
function B2(condition2){
return condition2 === B2 ? ... : 'doNext'
}
function C2(condition2){
return condition2 === C2 ? ... : 'doNext'
}
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
/**
* 函数交织(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
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)
}
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))))) // 一层层括号像极了洋葱皮
🎯传参问题!!
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")
{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")
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")
🐵小结展望
链接:
https://juejin.cn/post/6996811608756322334
如有侵权请联系小编删除
前端进阶学习圈
行业大牛“花果山技术团队”成员干货分享,行业热点解析、直播训练营、直播公开课、学习资源等,一键关注随时随地和行业大牛交流,快速提升技术能力、面试技巧,升职加薪快人一步!
0篇原创内容
Official Account