解析防抖和节流

67 阅读2分钟

前言

防抖和节流总体上来说是减少函数的执行次数,优化浏览器的性能,因为有些功能不需要频繁的执行。例如onClick事件,onScroll事件。但二者不同,让我们来分别看看他们的具体功能。

函数的防抖

对于频繁触发某个操作,我们只识别一次,即只触发执行一次函数。

主体思路

在当前点击完成后,我们等待一段的时间,看是否还会触发第二次,如果没有触发第二次,属于非频繁操作,我们直接执行想要执行的函数func;如果触发了第二次,则以前的不算了,从当前这次再开始等待...

function debounce(func, wait = 300, immediate = false) {
  let timer = null;

  return function anonymous(...params) {
    let now = immediate && timer === null;

    // 每次点击都把之前设置的定时器清除
    clearTimeout(timer);

    // 重新设置一个新的定时器监听wait时间内是否触发第二次
    timer = setTimeout(() => {
      // 手动让其回归到初始状态
      timer = null;
      // wait这么久的等待中,没有触发第二次
      !immediate ? func.call(this, ...params) : null;
    }, wait);

    // 如果是立即执行
    now ? func.call(this, ...params) : null;
  };
}

参数:

  1. func[function]:最后要触发执行的函数;
  2. wait[number]:“频繁”设定的界限;
  3. immediate[boolean]:默认多次操作,我们识别的是最后一次,但是immediate=true,让其识别第一次

通过闭包返回一个可以被调用执行的函数

不采用防抖:

function handle() {
  console.log("OK");
}
submit.onclick = handle

点击多少次就执行多少次

采用防抖:

submit.onclick = debounce(handle, 300, true);

肉眼可以看到减少了执行次数

函数节流

在一段频繁操作中,可以触发多次,但是触发的频率由自己指定。

function throttle(func, wait = 300) {
  let timer = null,
    previous = 0; // 记录上一次操作的时间

  return function anonymous(...params) {
    let now = new Date();
    let remaining = wait - (now - previous); //记录还差多久达到我们一次触发的频率
    if (remaining <= 0) {
      // 两次操作的间隔时间已经超过wait了
      timer = null;
      previous = now;
      func.call(this, ...params);
     // 通过!timer判断是否进入下一次函数执行,如果上一次没有执行,则拒绝执行
    } else if (!timer) {
      // 两次操作的间隔时间还不符合触发的频率
      timer = setTimeout(() => {
        timer = null;
        previous = new Date();
        func.call(this, ...params);
      }, remaining);
    }
  };
}

以onscroll函数来测试:

window.onscroll = handle; //每一次滚动过程中,浏览器有最快反应时间(5~6ms 13~17ms),只要反应过来就会触发执行一次函数(此时触发频率5ms左右)

可以看到onscroll事件绑定的函数被多次触发。

接下来使用节流包装的handle函数来测试

window.onscroll = throttle(handle);

可以看到onscroll事件绑定的函数只触发了十次左右。

总结

防抖和节流减少了函数执行次数,优化了浏览器性能。 点击事件一般采用防抖。滚动、文本框输入事件采用节流。