防抖 & 节流 笔记

参考

参考博文注释很详细到位

防抖 (debounce)

对于短时间内连续触发的事件,
防抖的含义就是让某个时间期限内,
事件处理函数只执行一次。

在第一次触发事件时,不立即执行函数,而是给出一个期限值(延迟执行)

/*
* fn [function] 需要防抖的函数
* delay [number] 毫秒,防抖期限值
*/
function debounce(fn, delay) {
  let timer = null; // 借助闭包
  return function() {
    if (timer) {
      // 进入该分支语句,说明当前正在一个计时过程中,
      // 并且又触发了相同事件。所以要取消当前的计时,
      // 重新开始计时
      clearTimeout(timer);
    }
    // 当前没有在计时,开始一个计时
    timer = setTimeout(fn, delay);
  }
}

// 监听浏览器滚动事件,返回当前滚动条与顶部的距离
function showTop() {
  let scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
  console.log('滚动条位置:' + scrollTop);
}
// delay 根据实际需要来配置
window.onscroll = debounce(showTop, 1000)

节流 (throttle)

如果在限定时间段内,不断触发滚动事件
(比如某个用户闲着无聊,按住滚动不断的拖来拖去),
只要不停止触发,理论上就永远不会输出当前距离顶部的距离。

? 即使用户不断拖动滚动条,也能在某个时间间隔之后给出反馈

设计一种类似控制阀门一样定期开放的函数,
也就是让函数执行一次后,在某个时间段内暂时失效,
过了这段时间后再重新激活(类似于技能冷却时间)

function throttle(fn, delay) {
  let valid = true;
  return function () {
    if (!valid) {
      // 休息时间 暂不接客
      // 技能冷却中
      return false;
    }
    // 工作时间,执行函数并且在间隔期内把状态位设为无效
    valid = false;
    // * 可以把状态位换成时间戳
    //   利用时间戳差值是否大于指定间隔时间来做判定。
    // * 可以直接将setTimeout的返回的标记当作判断条件
    //   判断当前定时器是否存在,
    //       如果存在表示还在冷却,并且在执行fn之后消除定时器表示激活
    setTimeout(() => {
      fn()
      valid = true;
    }, delay);
  }
}

// 监听浏览器滚动事件,返回当前滚动条与顶部的距离
// 如果一直拖动滚动条进行滚动,那么会以1s的时间间隔,
// 持续输出当前位置和顶部的距离
function showTop() {
  let scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
  console.log('滚动条位置:' + scrollTop);
}
window.onscroll = throttle(showTop, 1000);

other

  • 搜索框input事件

    例如要支持输入实时搜索可以使用节流方案(间隔一段时间就必须查询相关内容)
    或者实现输入间隔大于某个值(如500ms),就当作用户输入完成,然后开始搜索,
    具体使用哪种方案要看业务需求.

  • 页面resize事件

    常见于需要做页面适配的时候。
    需要根据最终呈现的页面情况进行dom渲染
    (这种情形一般是使用防抖,因为只需要判断最后一次的变化情况)

  • 实际还要考虑作用域 参数传递 argument 以及上下文环境 apply