写作不易,Star是最大鼓励,感觉写的不错的可以给个Star⭐,请多多指教。本博客的Github地址

函数组合在函数式编程中被称为组合(composition)。

为什么要使用函数组合?

在业务需求中,我们经常会根据不同的业务需求封装各种不同的函数。每当业务需求发生变更的时候,之前封装的函数可能不满足当前的业务需求场景。这时,我们并不应该根据业务需求对之前封装的函数进行修改,因为这就破坏了设计模式中的开闭原则。

推荐做法:当业务需求发生变更的时候,不要对之前封装的代码进行更改,而是要新增响应的函数功能,然后把函数进行重新组合来实现不同的业务场景需求。

开闭原则:软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的。

实现函数组合

核心思路:定义一个函数,该函数接收若干个函数作为参数,并返回一个新函数。新函数执行时,按照自右向左的顺序依次执行传入compose中的函数,每个函数的执行结果作为下一个函数的输入,直至最后一个函数的输出作为最终的输出结果。

const str = 'hello';

function stringToUpper(str) {
    return str.toUpperCase();
}

function stringReverse(str) {
    return str.split('').reverse().join('');
}

function getThreeCharacters(str) {
    return str.slice(0, 3);
}

function stringToArray(str) {
    return str.split('');
}

// args参数是接收的函数参数数组
const compose = (...args) => {
    return function(data) {
        return args.reduceRight((initialValue, cb) => { // 基于reduceRight实现函数的自右向左依次执行
            return cb(initialValue);
        }, data);
    }
};

// 先将字符串转为大写->再将字符串反转->截取前3个字符
// 可以通过将上述除了compose之外的四个函数任意组合来实现不同的业务需求
const stringToUpperAndReverse = compose(getThreeCharacters, stringReverse, stringToUpper);

console.log(stringToUpperAndReverse(str));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

实现管道(pipeline)

在上述函数组合实现中,参数函数的执行顺序是自右向左的,即最右侧的函数最先执行,最左侧的函数最后执行。

从左至右处理数据流的过程称之为管道(pipeline)。管道(pipeline)的实现同compose的实现方式类似,两者的区别仅仅是数据流的方向不同而已。如果想要实现自左向右的执行方式,即最左侧的函数最先执行,最右侧的函数最后执行。对比compose函数的实现,仅仅需要将reduceRight替换为reduce即可:

const pipe = (...args) => {
    return function(data) {
        return args.reduce((initialValue, cb) => { // 基于reduce实现函数的自左向右依次执行
            return cb(initialValue);
        }, data);
    }
};
1
2
3
4
5
6
7

参考文档

  1. 「前端进阶」彻底弄懂函数组合