蒙娜丽莎, 她是谁? 带你揭开compose的神秘面纱!

381 阅读3分钟

知识科普

compose就是按从右至左的顺序依次执行函数数组里面的每一个函数, 并把上一次函数执行返回的结果作为下一次函数的参数。其中只有第一个函数是多元函数(可以接收多个参数)

背景介绍

js是一门非常灵活的语言, 集合了好多语言的特性和多种编程模式, 对于componse的实现, 就有非常多的思路, 每一种思路独树一帜, 各领风骚, 实现之后, 犹如拨开云雾见天日, 一个字, 爽! 包括但不至于以下实现思路:

  1. 面向过程(递归)
  2. 函数组合(reduce)
  3. Promise(sequence)

1.面向过程/递归

这个思路就是使用递归的过程思想,不断地检测队列中是否还有任务,如果有任务就执行, 并把执行结果往后传递. 优点: 直观上最容易结束和理解, 也易于实现 缺点: 无法预知任务何时结束(实际上这也是递归的缺点)

const compose = function(args) {
	let length = args.length
	let count = length - 1
	let result 
	return function f1(...argsF1) {
		result = args[count].apply(this, argsF1)
		if(count <=0 ) {
			return result
		}
		count --
		return f1.call(null, result)
	}
}
			
let a = function(a, b){ return a + b}
let b = function(a){ return a * a}
let c = function(a){ return a * a +10}
let steps = [c, b, a]
let composeFunc = compose(steps)
console.log(composeFunc(1, 2)) // 91

2.函数组合/reduce

compose是函数式编程中使用较多的一种写法, 它把逻辑解耦在各个函数中, 通过compose的方式组合函数, 将外部数据依次通过各个函数的加工, 生成结果. 话不多说, 上代码 --

let a = function(a, b){console.log(1); return a + b}
let b = function(a){ console.log(2); return a * a}
let c = function(a){ console.log(3); return a * a + 10}
let steps = [c, b, a]
let reducer = (acc, cur) =>  (...x) => acc(cur(...x))
let compose = steps.reduce(reducer)
console.log(compose(1, 2)) // 91

参考来源: 函数式编程中的compose zhuanlan.zhihu.com/p/103515893

3.Promise

上代码

let a = function(a, b){console.log(1); return a + b}
let b = function(a){ console.log(2); return a * a}
let c = function(a){ console.log(3); return a * a + 10}
let steps = [c, b, a]
const compose = function(...steps) {
	let init = steps.pop()
	return function(...args) {
	       return steps.reverse().reduce(function(sequence, func) {
		       return sequence.then(function(result) {
			      return func.call(null, result)
		       })
		}, Promise.resolve(init.apply(null, args)))
       }
}
console.log(compose(...steps)(1, 2).then(res => console.log(res))) // 91 

注意事项: 因为then回调返回的是一个promise实例对象, 所以最后需要加多一个then回调来拿结果

使用compose的好处

  1. 提高函数的独立重用和可维护性
  2. 增强功能的扩展性

在实际业务中的应用

包括但不限于以下:

  1. 在需要处理复杂数据转换的时候, 比如后台给前端的响应数据转换到页面交互展示需要的数据的时候, 转换数据的逻辑可能比较复杂, 这个时候通常需要拆分成多个颗粒度比较小的函数来分别处理. 在提交表单数据的时候, 可能又要进行一次复杂转换, 把页面交互展示的数据格式转换成后台所需的数据格式.

  2. 在对一些文件做前端处理例如做excel文件的上传下载处理或者需要转换成table展示的时候, 中间步骤比较多, 可能需要做层层转换处理. 比如excel数据转table的时候, 可能要对excel中第一行的 每一个单元格做英文到中文的转换(对应table的每一列的表头), 除第一行的每一个单元格可能要做非空效 验或者其它规则校验(注意: 这里的excel是人工编辑上传的, 所以要做多种规则校验). 后台json数据转table同理.

  3. 为便于功能实现, 针对第三方库的封装, 中间过程比较复杂的场景

  4. webpack中的loader就是用的compose, 从右至左依次处理

总结:

1. 个人认为第二种是最佳实践, 原因包括但不限于: 代码量也最少, 使用最简便(只有一个'()'),稍有点小缺点是可读性略差, 使用之前需要彻底理解这种思路的实现机制和过程.

2. 使用递归的方法对新人最友好

3. 后面两种方法使用的是ES6的语法, 需要注意浏览器兼容