如果一个功能需要多个细粒度的函数进行组合实现,我们和很容易写出洋葱代码:h(g(f(x)))
例如:获取数组的最后一个元素再转换成大写字母,_.upperCase(_.last(arr))
函数组合可以让我们写出这样嵌套的代码
函数组合可以看成城市地下管道
下面这张图表示程序中使用函数处理数据的过程,给 fn 函数输入参数 a,返回结果 b。可以想想 a 数据通过一个管道得到了 b 数据。当 fn 函数比较复杂的时候,我们可以把函数 fn 拆分成多个小函数,此时多了中间运算过程产生的 m 和 n。
下面这张图中可以想象成把 fn 这个管道拆分成了3个管道 f1, f2, f3,数据 a 通过管道 f3 得到结果 m,m再通过管道 f2 得到结果 n,n 通过管道 f1 得到最终结果 b
伪代码
fn = compose(f1, f2, f3)
b = fn(a)
函数组合
如果一个函数要经过多个函数处理才能得到最终值,这个时候可以把中间
过程的函数合并成一个函数
- 函数就像是数据的管道,函数组合就是把这些管道连接起来,让数据穿过多个管道形成最终结果
- 函数组合默认是从右到左执行
lodash 的函数组合
我们使用lodash 函数组合方法,将数组最后一个元素大写
var _ = require('lodash');
let arr = ["hcj", 'wcj'];
let compose = _.flowRight(_.upperCase ,_.last);
console.log(compose(arr));
函数组合原理
- 接收不固定数量的函数形参,返回一个函数,接受一个参数
- 反转函数形参,依次调用,返回结果
let arr = ["hcj", 'wcj'];
function compose(...args) {
return function(value) {
return args.reverse().reduce(function(total, fn){
return fn(total);
}, value);
}
}
let composeFn = compose(_.upperCase ,_.last);
console.log(composeFn(arr));
结合律
和数学一样,函数组合也支持结合律
// 结合律(associativity) ture
let associative = compose(compose(f, g), h) == compose(f, compose(g, h))
调试
在我们使用组合时,如果结果和我们预期不一致,我们如何进行调试?以下面例子来说明:
// NEVER SAY DIE ---> never-say-die
var _ = require('lodash');
let str = "NEVER SAY DIE"
let split = _.curry((sep, str) => _.split(str, sep));
let map = _.curry((fn, arr) => _.map(arr,fn));
let join = _.curry((sep, arr) => _.join(arr, sep));
// debug 函数
let trace = _.curry((flag, data) => {
console.log(flag,data)
return data;
});
let compose = _.flowRight(trace("join") ,join("-"),trace("map") ,map(_.lowerCase) ,trace("split") ,split(" "));
console.log(compose(str));