前言
今天主要介绍函数式编程中有一个比较重要的概念就是函数组合(compose),组合多个函数,同时返回一个新的函数。调用时,组合函数按顺序从右向左执行。右边函数调用后,返回的结果,作为左边函数的参数传入,严格保证了执行顺序,这也是compose 主要特点。另外介绍一下build设计模式,大家可以自行体会一下其中的异同。
组合两个函数
compose 非常简单,通过下面示例代码,就非常清楚
function compose (f, g) {
return function(x) {
return f(g(x));
}
}
var arr = [1, 2, 3],
reverse = function(x){ return x.reverse()},
getFirst = function(x) {return x[0]},
compseFunc = compose(getFirst, reverse);
compseFunc(arr); // 3
组合任意个函数
上面组合了两个函数的compose,也让我们了解了组合的特点,接着我们看看如何组合更多的函数,因为在实际应用中,不会像入门介绍的代码那么简单。
主要注意几个关键点:
利用arguments的长度得到所有组合函数的个数
reduce 遍历执行所有函数。
var compose = function() {
var args = Array.prototype.slice.call(arguments);
return function(x) {
if (args.length >= 2) {
return args.reverse().reduce((p, c) => {
return p = c(p)
}, x)
} else {
return args[1] && args[1](x);
}
}
}
// 利用上面示例 测试一下。
var arr = [1, 2, 3],
reverse = function(x){ return x.reverse()},
getFirst = function(x) {return x[0]},
trace = function(x) { console.log('执行结果:', x); return x}
compseFunc = compose(trace, getFirst, trace, reverse);
compseFunc(arr);
// 执行结果: (3) [3, 2, 1]
// 执行结果: 3
// 3
如此实现,基本没什么问题,变量arr 在管道中传入后,经过各种操作,最后返回了结果。
function compose(...funcs) {
return funcs.reduce((prev, cur) => {
return (...args) => prev(cur(...args))
})
}
const toUpperCase = function(x) { return x.toUpperCase(); };
const exclaim = function(x) { return x + '!'; };
var shout = compose(exclaim, toUpperCase);
shout("send in the haorooms");
pipe管道的认识
认识pipe
函数式编程(FP)里面跟compose类似的方法,就是pipe。
pipe,主要作用也是组合多个函数,称之为'流或者管道, 肯定得按照正常方法,从左往右调用函数,与compose 调用方法相反。
ES6 实现Compose function
先看下compose 最基础的两参数版本,
const compose = (f1, f2) => value => f1(f2(value));
利用箭头函数,非常直接的表明两个函数嵌套执行的关系,
接着看多层嵌套。
(f1, f2, f3...) => value => f1(f2(f3));
实现pipe
前面提到pipe 是反向的compose,pipe正向调用也导致它实现起来更容易。
pipe = (...fns) => x => fns.reduce((v, f) => f(v), x)
一行代码就实现了pipe, 套用上面抽象出来的表达式,reduce刚好正向遍历所有函数, 参数x作为传递给函数的初始值, 后面每次f(v)执行的结果,作为下一次f(v)调用的参数v,完成了函数组合调用。
或者,可以把函数组合中,第一个函数获取参数后,得到的结果,最为reduce遍历的初始值。
pipe = (fn,...fns) => (x) => fns.reduce( (v, f) => f(v), fn(x));
利用es6提供的rest 参数 ,用于获取函数的多余参数.提取出第一个函数fn,多余函数参数放到fns中,fns可以看成是数组,也不用像arguments那种事先通过Array.prototype.slice.call转为数组,arguments对性能损耗也可以避免。 fn(x) 第一个函数执行结果作为reduce 初始值。
build模式
下面介绍一下build模式,
build模式案例代码如下:
let Ctx = function (a1, a2, a3) {
this.a1 = a1;
this.a2 = a2;
this.a3 = a3;
}
let CtxBuilder = function () {
let a1, a2, a3;
return {
setA1: function (a1) {
this.a1 = a1;
return this;
},
setA2: function (a2) {
this.a2 = a2;
return this;
},
setA3: function (a3) {
this.a3 = a3;
return this;
},
build: function () {
return new Ctx(this.a1, this.a2, this.a3)
}
}
}
let ctxBuilder = new CtxBuilder().setA1('a1')
.setA2('a2')
.setA3('a3');
let ctx = ctxBuilder.build();
大家一看就懂。
小结
上面讲了组合多个函数,及build模式,打包多个函数的方式,这些方式在工作中经常有用到,因此在这里介绍一下。希望对大家有帮助,本文是haorooms博客整理,欢迎关注前端博客,haorooms博客。