高阶函数

函数柯里化

一遍编程题引发的思考

实现 sum 函数使得下面输出结果一致

  1. sum(1,2,3).sumOf()
  2. sum(2,3)(1).sumOf()
  3. sum(2)(1)(3).sumOf()

第一反应是柯里化

  1. function add(a, b) {
  2. return a + b;
  3. }
  4. function curry(add) {
  5. let arr = [];
  6. return function reply() {
  7. let arg = [...arguments];
  8. arr = arr.concat(arg);
  9. if (arg.length == 0) {
  10. return arr.reduce((p, c) => (p = add(p, c)), 0);
  11. } else {
  12. return reply;
  13. }
  14. };
  15. }
  16. let sum = curry(add);
  17. console.log(sum(1, 2, 3)()); // 6
  18. console.log(sum(2, 3)(1)()); //12

显然 这一没有 sumof 实现 也完全复用了

防抖和节流

强调一下它们的区别:

  • 节流是让事件处理函数隔一个指定毫秒再触发
  • 防抖则忽略中间的操作,只响应用户最后一次操作

函数拦截器

对废弃 API 进行提示增加

一个小细节,定义notice = once(console.warn),用notice输出,这样的话,调用相同的函数只会在控制台显示一遍警告,就避免了输出太多重复的信息。

  1. function deprecate(fn, oldApi, newApi) {
  2. const message = `The ${oldApi} is deprecated.
  3. Please use the ${newApi} instead.`;
  4. const notice = once(console.warn);
  5. return function(...args) {
  6. notice(message);
  7. return fn.apply(this, args);
  8. }
  9. }

WEB 拦截器

  1. function intercept(fn, {beforeCall = null, afterCall = null}) {
  2. return function (...args) {
  3. if(!beforeCall || beforeCall.call(this, args) !== false) {
  4. // 如果beforeCall返回false,不执行后续函数
  5. const ret = fn.apply(this, args);
  6. if(afterCall) return afterCall.call(this, ret);
  7. return ret;
  8. }
  9. };
  10. }

axios 请求队列拦截器的实现

高阶函数的范式

  1. function HOF0(fn) {
  2. return function(...args) {
  3. return fn.apply(this, args);
  4. }
  5. }

HOF0是高阶函数的等价范式,或者说,HOF0修饰的函数功能和原函数fn的功能完全相同。因为被修饰后的函数就只是采用调用的this上下文和参数来调用fn,并将结果返回。也就是说,执行它和直接执行fn完全没区别。

compose

高阶函数可以任意组合,形成更强大的功能。

另外,像这样fn1(fn2(fn3(args...)))嵌套的写法,我们也可以用高阶函数改变成更加友好的形式: 也就是 compose

redux 版本(同步函数)

  1. function f1(arg) {
  2. console.log("f1", arg);
  3. return arg;
  4. }
  5. function f2(arg) {
  6. console.log("f2", arg);
  7. return arg;
  8. }
  9. function f3(arg) {
  10. console.log("f3", arg);
  11. return arg;
  12. }
  13. const res = f1(f2(f3("omg")));
  14. console.log("res", res); //sy-log
  15. // f3 omg
  16. // f2 omg
  17. // f1 omg
  18. // res omg

优化写法

  1. function compose(...funcs) {
  2. if (!funcs.length) {
  3. return (arg) => arg;
  4. }
  5. return funcs.reduce((a, b) => (...args) => a(b(...args)));
  6. }
  7. compose(f1, f2, f3)("omg");
  8. // f3 omg
  9. // f2 omg
  10. // f1 omg

koa 版本(异步中间件)

例子

  1. function compose(middlewares){
  2. return () = >{
  3. }
  4. }
  5. async function fn1(next) {
  6. console.log("fn1");
  7. await next();
  8. console.log("end fn1");
  9. }
  10. async function fn2(next) {
  11. console.log("fn2");
  12. await delay();
  13. await next();
  14. console.log("end fn2");
  15. }
  16. function fn3(next) {
  17. console.log("fn3");
  18. }
  19. function delay() {
  20. return new Promise((reslove, reject) => {
  21. setTimeout(() => {
  22. reslove();
  23. }, 2000);
  24. });
  25. }
  26. const middlewares = [fn1, fn2, fn3];
  27. const finalFn = compose(middlewares);
  28. finalFn();

compose 实现

  • compose 接受一个 函数数组
  • 返回一个 具有一个 next 函数参数的 函数
  • 用 Promise 包装,
  • 考虑边界条件
  1. function compose(middlewares){
  2. return () = >{
  3. dispatch(0)
  4. }
  5. function dispatch(i){
  6. let fn = middlewares[i]
  7. if(!fn) return Promise.resolve()
  8. return fn(()=> Promise.resolve(dispatch(i +1 )))
  9. }
  10. }