高阶函数 - Higher Order Function

一个函数 如果输入参数包含函数 或 返回值是函数,就称为高阶函数。

这篇文章介绍高阶函数的一个子集:输入 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 memorize = fn => {
      const memory = {};
      return arg => {
        if (memory[arg]) return memory[arg];
        else {
          memory[arg] = fn(arg);
          return memory[arg];
        }
      };
    };
    const fib = memorize(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的高阶组件,道理是一样的。

转载于:https://www.cnblogs.com/apolis/p/11079084.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值