zoukankan      html  css  js  c++  java
  • 高阶函数

    一个函数如果有 参数是函数 或 返回值是函数,就称为高阶函数。

    这篇文章介绍高阶函数的一个子集:输入 fn,输出 fn'。按 fnfn' 功能是否一致,即相同输入是否始终对应相同输出,把这类高阶函数的作用分为两种:

    1. 包装函数:功能一致
    2. 修改函数:功能不一致

    包装函数

    从斐波那契数列开始。

    const fib = n =>
      n <= 1 ? 1 : fib(n - 1) + fib(n - 2);
    
    fib(42);
    

    记录执行时间

    • 普通青年

      const fib = n =>
        n <= 1 ? 1 : fib(n - 1) + fib(n - 2);
      
      console.time("fib");
      fib(42);
      console.timeEnd("fib");
      
    • 函数式青年

      const timed = fn => (...args) => {
        console.time(fn.name);
        const result = fn(...args);
        console.timeEnd(fn.name);
        return result;
      };
      const fib = n =>
        n <= 1 ? 1 : fib(n - 1) + fib(n - 2);
      
      timed(fib)(42);
      

    优化性能

    • 普通青年

      const memory = {};
      const fib = n => {
        if (n <= 1) return 1;
        else {
          if (memory[n]) return memory[n];
          else {
            memory[n] = fib(n - 1) + fib(n - 2);
            return memory[n];
          }
        }
      };
      const timed = fn => (...args) => {
        console.time(fn.name);
        const result = fn(...args);
        console.timeEnd(fn.name);
        return result;
      };
      timed(fib)(42);
      
    • 函数式青年

      const memoize = fn => {
        const memory = {};
        return arg => {
          if (memory[arg]) return memory[arg];
          else {
            memory[arg] = fn(arg);
            return memory[arg];
          }
        };
      };
      const fib = memoize(n =>
        n <= 1 ? 1 : fib(n - 1) + fib(n - 2)
      );
      const timed = fn => (...args) => {
        console.time(fn.name);
        const result = fn(...args);
        console.timeEnd(fn.name);
        return result;
      };
      timed(fib)(42);
      

    修改函数

    once

    场景:
    发送请求,如果后台返回 session 超时,弹出重新登录提示框。
    发出多个请求,都 session 超时,只希望弹一个重新登录提示框。

    const once = fn => {
      let executed = false;
      return (...args) => {
        if (!executed) {
          executed = true;
          fn(...args);
        }
      };
    };
    const showLogoutWin = once(() => {
      // ...
    });
    

    debounce

    场景:
    输入框 change 事件触发向后台查询。
    为消除不必要查询,用户连续输入时不触发查询,
    当 200ms 内没有新的输入时,才向后台查询。

    const debounce = (fn, ms = 200) => {
      let timeoutId;
      return (...args) => {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => fn(...args), ms);
      };
    };
    

    更多实际场景

    validateRequired

    场景:
    根据 rule.required 判断空值时是否报错,
    这段逻辑出现在多个 validator 中。

    const ipv4Validator = (rule, value, callback) => {
      if (value) {
        if (ipv4RegExp.test(value)) {
          callback();
        } else {
          callback("请输入合法IP");
        }
      } else {
        if (rule.required) {
          callback("该域为必填项");
        } else {
          callback();
        }
      }
    };
    

    把判空的逻辑提取到高阶函数中:

    const validateRequired = (
      validator,
      msg = "该域为必填项"
    ) => (rule, value, callback) => {
      if (value) {
        validator(rule, value, callback);
      } else {
        if (rule.required) {
          callback(msg);
        } else {
          callback();
        }
      }
    };
    
    const ipv4Validator = validateRequired(
      (rule, value, callback) => {
        if (ipV4Regexp.test(value)) {
          callback();
        } else {
          callback("请输入合法IP");
        }
      }
    );
    

    tryUntilSucceeded

    场景:
    因为网络不稳定,请求可能出错。
    出错后重新请求,直到得到响应为止。

    let res;
    while (true) {
      try {
        res = await get(path);
        break;
      } catch (err) {
        console.log(err);
      }
    }
    

    把出错重试的逻辑提取到高阶函数中:

    const tryUntilSucceeded = fn => async (
      ...args
    ) => {
      while (true) {
        try {
          return await fn(...args);
        } catch (err) {
          console.log(err);
        }
      }
    };
    
    const enhancedGet = tryUntilSucceeded(get);
    const enhancedPost = tryUntilSucceeded(post);
    
    const resGet = await enhancedGet(path);
    const resPost = await enhancedPost(path);
    

    小结

    两个代码块一样,把这个代码块提取出来,封成一个函数,减少代码重复,这个技巧大家都知道;两段代码流程一样,用高阶函数把公共流程提取出来,减少代码重复,这个技巧知道的人就不多了。可以类比 react 高阶组件,道理是一样的。

  • 相关阅读:
    一个.java源文件中可以有多个类吗?(内部类除外)有什么条件?
    接口中定义的变量为什么是常量
    关于String s = new String("xyz");创建了几个字符串对象?的问题
    java面试题之----JVM架构和GC垃圾回收机制详解
    Object中的clone方法
    C/S与B/S架构的区别和优缺点
    EJB是什么?
    JNDI是什么,怎么理解
    java中什么是上下文(servletContext)
    java面试题----String、StringBuffer、StringBudder区别
  • 原文地址:https://www.cnblogs.com/apolis/p/11079084.html
Copyright © 2011-2022 走看看