前言
关于JS函数式编程,之前已经讨论了其中的柯里化,另外还有一个偏函数,跟柯里化非常相似
柯里化和偏函数的区别
偏函数固定了部分参数,生成另外一个参数更少的方法,比如bind()
今天再学习以下compose和pipe
compose和pipe
应用场景:有一些公用函数,现在需要执行一系列操作,就需要不停的调用这些公用函数,所以就会出现一个接一个的调用,如果有一个函数可以把这些需要调用的函数按照顺序链接起来,直接调用这个函数不就可以了。
// 公共函数,顺序由上至下
let greeting = (firstname,lastname) => "h1,"+firstname+lastname;
let toUpper = str => str.toUpperCase();
let trimSpace = str => str.replace(/\s+/g,'');
现在用一个函数把这三个函数包装起来,这个包装函数可以是compose或者pipe
pipe和compose的区别
pipe函数包装函数是根据参数顺序包装pipe([greeting,toUpper,trimSpace]),而compose是根据参数倒序包装compose([trimSpace,toUpper,greeting])
pipe 的实现
let pipe = function (funcs) {
let len = funcs.length;
// 判断参数是否都是函数
let index = len;
while (index--) {
if(typeof funcs[index] !== 'function') {
throw new TypeError('Excepted a function');
}
}
return function(...args) {
let index = 0;
// 如果没有包装任何一个函数,则返回调用包装函数的第一个参数
let result = len ? funcs[index].apply(this,args):args[0];
// 循环绑定
while (++index<len) {
result = funcs[index].call(this,result);
}
return result;
}
};
// compose的实现
let compose = function(funcs) {
return pipe(funcs.reverse());
};
let fn = flow([greeting,toUpper,cutTrim]);
console.log(fn('y i n g','m u'));
上面是用循环实现的,下面用递归的思想实现
let pipe = function (funcs) {
let len = funcs.length;
let index = 0;
let result;
return function f1(...args) {
// 第一次args是传进来的参数,之后args==result
result = funcs[index].apply(this, args);
if (index >= len - 1) {
// 重置下标为0
index = 0;
return result;
}
index++;
return f1.call(null, result);
}
};
// compose的实现
let compose = function (args) {
return pipe(args.reverse());
};
let fn1 = compose([trimSpace, toUpper, greeting]);
console.log(fn1('h u a', 'd a o'));
reduce实现
另外,还可以用reduce来实现
Array.prototype.reduce
reduce跟map很相似,但是数组进行map操作的时候会把每一个元素都被回调函数‘作用’一次,然后返回一个新的数组,而数据执行reduce时,每个元素被回调函数‘作用’一次后都把返回的结果传递给下一个元素的回调函数(即回调函数的第一个参数),然后这个结果跟下一个元素继续‘作用’
基本语法
// 接收两个参数,第一个执行函数,第二个初始值(可选)
arr.reduce(callback[, initialValue]);
// callback共接收4个参数:
// 1. accumulator 上一次回调的返回值
// 2. currentValue 当前正在处理的元素
// 3. index (可选) 当前元素的索引
// 4. array (可选) 当前调用reduce的数组
示例
// 数组求和,初始值为10
let arr = [1,2,3,4,5];
let sum = arr.reduce((accumulator,currentValue) => accumulator+currentValue,10);
console.log(sum) // 25
compose和pipe实现剖析
假如要把函数a,b,c,d串联起来,其实实现的最终结果就是a(b(c(d(args)))),用reduce实现就是把函数a,b,c,d当做一个数组,然后进行遍历,初始值为函数d的参数,第一次回调返回d(args),然后把这个结果作为第二次回调的参数作用于c,返回c(d(args)),以此类推...
代码实现
let pipe = (...funcs) => (...args) => funcs.reduce((arg, fuc, index) => index == 0 ? fuc.apply(this, arg) : fuc.call(this, arg), args);
let fn = pipe(greeting, toUpper, cutTrim);
console.log(fn('h u a', 'd a o'))