函数式编程之compose
前言
在日常工作中,经常会出现这样的情况:一个数据处理会经常某些步骤,比如字符串转大写处理、增加后缀处理等步骤;我们很容易的能写出这样的处理函数:
function handleString(str) {
//step 1:
let upperString = str.toUpperCase();
//step 2:
let result = upperString + '!';
return result;
}
每个处理步骤都可能需要在某种情况下使用,那么这种代码写法难以达到复用;将每个步骤单独作为一个函数,也需要不同的流程处理函数,如下:
//step 1:
function toUpperCase(str) {
return str.toUpperCase();
}
// step2:
function addExteranl(str) {
return str + '!';
}
function handleString(str) {
let upperString = toUpperCase(str);
let result = addExteranl(upperString);
return result;
}
那么是否有更优雅的处理方式呢,那就是下文要介绍的compose
。
compose 初探
compose 在 redux 插件实现中有广泛的运用,我们首先来看一下 compose 的实现:
// refs: https://github.com/reduxjs/redux/blob/master/src/compose.ts
function compose(...funcs: Function[]) {
if (funcs.length === 0) {
// infer the argument type so it is usable in inference down the line
return <T>(arg: T) => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args: any) => a(b(...args)))
}
-
参数 : 函数fn1,fn2... -
返回值:一个合成函数
compose 函数的作用是传入一个函数数组,合成一个最终的函数;返回的结果会按照函数参数从右到左的顺序调用,如:
let func = compose(addExteranl,toUpperCase)
func('hello world')
func 执行会首先调用 toUpperCase,将调用结果作为参数传入 addExternal。根据数组中参数不同,可以组合不同的数据处理方式,使得更加灵活。
compose 实现原理
compose 是如何实现将不同函数组合的呢,在了解之前先看下 Array.reduce
arr.reduce(callback(accu, currentValue)),数组arr 中每个元素都会去执行提供的callback 元素,并将执行结果作为下次callback 的第一个参数,比如使用reduce 实现数组累加:
let arr = [1, 2, 3];
// accu 第一次如果没指定则为数组中的第一个元素,下次为上次callback 执行累加的结果,accu 取值依次为 1, 3
// currentValue 数组中的元素
let result = arr.reduce((accu, currentValue) => accu + currentValue)
在了解reduce 实现之后具体来看 compose 核心实现
return funcs.reduce((a, b) => (...args: any) => a(b(...args)))
首次调用时是 funcs[0], funcs[1] 返回一个函数 accu2 : (... args) => funcs[0](funcs[1](...args))
第二次调用是 accu2, funcs[3] 返回一个函数 accu3: (... args) => accu2(funcs[3])
最终合成 合成 f1(f2(f3(xxx)))实现从右左的调用
总结
本文主要讲解使用 reduce 巧妙的实现compose 的过程,compose 在高阶组件、插件实现以及数据合成处理等场景中经常会使用,提高代码的易用性。
参考资料
reduce 介绍:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
redux 中关于 compose 实现:https://github.com/reduxjs/redux/blob/master/src/compose.ts