vlambda博客
学习文章列表

函数式编程之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