一、问题
- 在函数式编程当中有一个很重要的概念就是函数组合, 实际上就是把处理数据的函数像管道一样连接起来, 然后让数据穿过管道得到最终的结果。 例如:
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));