常用HOF | 青训营笔记

292 阅读3分钟

常用HOF | 青训营笔记

这是我参与「第四届青训营 」笔记创作活动的第5天

引入

HOF——即高阶函数,那么何为高阶函数?一个函数就可以接收另一个函数作为参数或者返回值为一个函数,这种函数就称之为高阶函数。例如,数组中的高阶函数如:map、filter、reduce、sort,都是以函数为参数对数组进行操作,大致的HOF函数框架如下:

// 高阶函数框架
function HOF0(fn) {
  // 内外两个函数效果是等价的
  return function (...args) {
    // 会加了一些操作
    return fn.apply(this, args);
  }
}

HOF特点

  • 以函数作为参数
  • 以函数作为返回值
  • 常用于作为函数装饰器

简单案例

Once函数:判断一个操作只执行一次

// 高阶函数:调用一个函数会return一个另一个函数
function once(fn) {
  // outer scape closure ...【形成一个闭包】
  return function (...args) {
    // inner scape【只执行一次】
    if (fn) {
      const ret = fn.apply(this, args);
      fn = null;  // 置空则下次执行不了
      return ret;
    }
  }
}

为了能够让“只执行一次”的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。这个过程我们称为过程抽象。

常用HOF

  1. 节流阀throttle
/**
 *
 * @param {*} fn 要进行节流操作的函数【写函数参数时无论是箭头函数还是普通函数格式,最后的this指向都一样是window】
 * @param {*} time 控制触发的间隔时间
 * @returns
 */
function throttle(fn, time = 500) {
  let timer;
  return function (...args) {   // 这里若是改为箭头函数则this会向外找对象window
    if (timer == null) {
      fn.apply(this, args);
      // 触发一个定时器使得函数保持在一段时间后再触发
      timer = setTimeout(() =>// 注意这里必须为箭头函数,否则若是fn.apply()写在其下则会指向window
        timer = null;
      }, time)
    }
  }
}
  1. 防抖debounce
/**
 * 【例子:隔几秒进行自动保存/鼠标移动小鸟跟着移动】
 * @param {*} fn 要进行防抖操作的函数【永远只执行最后一次调用】
 * @param {*} dur 控制间歇时间
 * @returns
 */
function debounce(fn, dur) {
  dur = dur || 100;
  var timer;
  return function () {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, arguments);
    }, dur);
  }
}
  1. 迭代器Iterator
/**
 * iterative()先由isIterable()函数来分析是否为可迭代对象然后再进行后续操作
 * @param {*} fn 将可迭代对象的子元素挨个进行操作【jq的API有提供类似的操作】
 * @returns
 */
const isIterable = obj => obj != null  && typeof obj[Symbol.iterator] === 'function';
function iterative(fn) {
  return function (subject, ...rest) {
    if (isIterable(subject)) {
      const ret = [];
      for (let obj of subject) {
        ret.push(fn.apply(this, [obj, ...rest]));
      }
      return ret;
    }
    return fn.apply(this, [subject, ...rest]);
  }
}
// 案例:实现给所有的els加颜色
const setColor = iterative((el, color) => {
  el.style.color = color;
});
const els = document.querySelectorAll('li:nth-child(2n+1)');
setColor(els, 'red');

为什么要使用高阶函数

  1. 纯函数:简单,无特殊输入,结果可预期;【高阶函数是纯函数 => 输入输出是固定的】

  2. 非纯函数:复杂,检测难【需要使用外部的参数 or 一个函数又依赖另外一个函数,需要检测多个函数】,因此鼓励使用纯函数【即高阶函数】,可以大大的提高可维护性