一、问题
- 在函数式编程当中有一个很重要的概念就是函数组合, 实际上就是把处理数据的函数像管道一样连接起来, 然后让数据穿过管道得到最终的结果。 例如:
const add1 = x => x + 1;
const mul3 = x => x * 3;
const div2 = x => x / 2;
div2(mul3(add1(add1(0)))); //=>3
- 而这样的写法可读性明显太差了,我们可以构建一个compose函数,它接受任意多个函数作为参数(这些函数都只接受一个参数),然后compose返回的也是一个函数,达到以下的效果:
const operate = compose(div2, mul3, add1, add1)
operate(0) //=>相当于div2(mul3(add1(add1(0))))
operate(2) //=>相当于div2(mul3(add1(add1(2))))
- 简而言之:compose可以把类似于f(g(h(x)))这种写法简化成compose(f, g, h)(x),请你完成 compose函数的编写
二、组合函数
- 老师reduce实现
- 不会产生闭包,每次返回结果
// 组合
function compose(...funcs) {
// funcs -> [div2, mul3, add1, add1]
return function operate(x) {
let len = funcs.length;
if (len === 0) { // 一个函数都没有
return x;
}
if (len === 1) { // 一个函数就直接执行就好了,不用reduce
return funcs[0](x);
}
// reduceRight 从右往左迭代
// 一开始 result是x ,这是要处理的数据
return funcs.reduceRight((result, item) => {
return item(result);
}, x);
};
}
const operate = compose(div2, mul3, add1, add1);
console.log(operate(0));
- react的源码
- 产生大量闭包
// 有惰性思想
function compose(...funcs) {
if(funcs.length === 0) {
return args => args;
}
if(funcs.length === 1) {
return funcs[0];
}
// 箭头函数套箭头函数
return funcs.reduce((a, b) => (...args) => a(b(...args)));
}
// 展开看
function compose(...funcs) {
if(funcs.length === 0) {
return args => {
return args; // 返回函数,(这个函数传什么,返回什么)
}
}
if(funcs.length === 1) {
return funcs[0]; // 返回第一个函数
}
// 每一次迭代,执行回调函数,都产生一个闭包,存储a和b,
// 返回的小函数中后期使用的a和b就是这个闭包中的
// 每次返回(第二层)的函数作为下一次的a, 层层包裹
return funcs.reduce((a, b) => {
// 第一次
// a -> div2
// b -> mul3
// return x=>a(b(x)) @1
// 第二次
// a -> @1
// b -> add1
// return x=>a(b(x)) @2
// 第三次
// a -> @2
// b -> add1
// return x=>a(b(x)) @3
// 执行的时候一层层剥洋葱
return (...args) => {
return a(b(...args));
}
});
}
const operate = compose(div2, mul3, add1, add1);
console.log(operate(0));
- 结合两者
function compose(...funcs) {
let len = funcs.length;
if (len === 0) {
return args => args;
}
if (len === 1) {
return funcs[0];
}
return function operate(...args) {
return funcs.reduceRight((result, item) => {
if (Array.isArray(result)) { // 是否展开
return item(...result);
}
return item(result);
}, args);
};
}
let operate = compose(div2, mul3, add1, add1);
console.log(operate(0));
- reduce原理
Array.prototype.reduce = function reduce(callback, initial) {
let self = this, // this -> arr(我们要处理的数组)
i = 0,
len = self.length,
item,
result;
if (typeof callback !== "function") {
throw new TypeError('callback must be an function!');
}
// 初始值不设置,让初始值是数组第一项,并且从数组第二项开始遍历
if (typeof initial === "undefined") {
initial = self[0];
i = 1;
}
result = initial;
// 循环数组中的每一项
for (; i < len; i++) {
item = self[i];
result = callback(result, item, i);
}
return result;
};
let arr = [10, 20, 30, 40];
console.log(arr.reduce((result, item, index) => {
return result + item;
}));
console.log(arr.reduce((result, item) => {
return result + item;
}, 0));