一、问题

  1. 在函数式编程当中有一个很重要的概念就是函数组合, 实际上就是把处理数据的函数像管道一样连接起来, 然后让数据穿过管道得到最终的结果。 例如:
  1. const add1 = x => x + 1;
  2. const mul3 = x => x * 3;
  3. const div2 = x => x / 2;
  4. div2(mul3(add1(add1(0)))); //=>3
  1. 而这样的写法可读性明显太差了,我们可以构建一个compose函数,它接受任意多个函数作为参数(这些函数都只接受一个参数),然后compose返回的也是一个函数,达到以下的效果:
  1. const operate = compose(div2, mul3, add1, add1)
  2. operate(0) //=>相当于div2(mul3(add1(add1(0))))
  3. operate(2) //=>相当于div2(mul3(add1(add1(2))))
  1. 简而言之:compose可以把类似于f(g(h(x)))这种写法简化成compose(f, g, h)(x),请你完成 compose函数的编写

二、组合函数

  1. 老师reduce实现
  2. 不会产生闭包,每次返回结果
  1. // 组合
  2. function compose(...funcs) {
  3. // funcs -> [div2, mul3, add1, add1]
  4. return function operate(x) {
  5. let len = funcs.length;
  6. if (len === 0) { // 一个函数都没有
  7. return x;
  8. }
  9. if (len === 1) { // 一个函数就直接执行就好了,不用reduce
  10. return funcs[0](x);
  11. }
  12. // reduceRight 从右往左迭代
  13. // 一开始 result是x ,这是要处理的数据
  14. return funcs.reduceRight((result, item) => {
  15. return item(result);
  16. }, x);
  17. };
  18. }
  19. const operate = compose(div2, mul3, add1, add1);
  20. console.log(operate(0));
  1. react的源码
  2. 产生大量闭包
  1. // 有惰性思想
  2. function compose(...funcs) {
  3. if(funcs.length === 0) {
  4. return args => args;
  5. }
  6. if(funcs.length === 1) {
  7. return funcs[0];
  8. }
  9. // 箭头函数套箭头函数
  10. return funcs.reduce((a, b) => (...args) => a(b(...args)));
  11. }
  12. // 展开看
  13. function compose(...funcs) {
  14. if(funcs.length === 0) {
  15. return args => {
  16. return args; // 返回函数,(这个函数传什么,返回什么)
  17. }
  18. }
  19. if(funcs.length === 1) {
  20. return funcs[0]; // 返回第一个函数
  21. }
  22. // 每一次迭代,执行回调函数,都产生一个闭包,存储a和b,
  23. // 返回的小函数中后期使用的a和b就是这个闭包中的
  24. // 每次返回(第二层)的函数作为下一次的a, 层层包裹
  25. return funcs.reduce((a, b) => {
  26. // 第一次
  27. // a -> div2
  28. // b -> mul3
  29. // return x=>a(b(x)) @1
  30. // 第二次
  31. // a -> @1
  32. // b -> add1
  33. // return x=>a(b(x)) @2
  34. // 第三次
  35. // a -> @2
  36. // b -> add1
  37. // return x=>a(b(x)) @3
  38. // 执行的时候一层层剥洋葱
  39. return (...args) => {
  40. return a(b(...args));
  41. }
  42. });
  43. }
  44. const operate = compose(div2, mul3, add1, add1);
  45. console.log(operate(0));
  1. 结合两者
  1. function compose(...funcs) {
  2. let len = funcs.length;
  3. if (len === 0) {
  4. return args => args;
  5. }
  6. if (len === 1) {
  7. return funcs[0];
  8. }
  9. return function operate(...args) {
  10. return funcs.reduceRight((result, item) => {
  11. if (Array.isArray(result)) { // 是否展开
  12. return item(...result);
  13. }
  14. return item(result);
  15. }, args);
  16. };
  17. }
  18. let operate = compose(div2, mul3, add1, add1);
  19. console.log(operate(0));
  1. reduce原理
  1. Array.prototype.reduce = function reduce(callback, initial) {
  2. let self = this, // this -> arr(我们要处理的数组)
  3. i = 0,
  4. len = self.length,
  5. item,
  6. result;
  7. if (typeof callback !== "function") {
  8. throw new TypeError('callback must be an function!');
  9. }
  10. // 初始值不设置,让初始值是数组第一项,并且从数组第二项开始遍历
  11. if (typeof initial === "undefined") {
  12. initial = self[0];
  13. i = 1;
  14. }
  15. result = initial;
  16. // 循环数组中的每一项
  17. for (; i < len; i++) {
  18. item = self[i];
  19. result = callback(result, item, i);
  20. }
  21. return result;
  22. };
  23. let arr = [10, 20, 30, 40];
  24. console.log(arr.reduce((result, item, index) => {
  25. return result + item;
  26. }));
  27. console.log(arr.reduce((result, item) => {
  28. return result + item;
  29. }, 0));