如果一个功能需要多个细粒度的函数进行组合实现,我们和很容易写出洋葱代码: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 函数组合方法,将数组最后一个元素大写
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));