函数防抖与函数节流

60 阅读2分钟

debounce-函数防抖:将一个弹簧按下,继续加压,继续按下,只会在最后放手的一瞬反弹。即我们希望函数只会调用一次,即使在这之前反复调用它,最终也只会调用一次而已。

throttle-函数节流:一个水龙头在滴水,可能一次性会滴很多滴,但是我们只希望它每隔 500ms 滴一滴水,保持这个频率。即我们希望函数在以一个可以接受的频率重复调用。

函数防抖

基本思想

某些代码不可以在没有间断的情况下连续重复执行。第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。当第二次调用该函数时,它会清除前一次的定时器并设置另一个。如果前一个定时器已经执行过了,这个操作(清除定时器)就没有任何意义。然而,如果前一个定时器尚未执行,其实就是将其替换为一个新的定时器。目的是只有在执行函数的请求停止了一段时间之后才执行。

基本实现

function debounce(fn, wait) {
  let timer;
  return function() {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      fn.apply(this, arguments)
    }, wait)
  }
}

函数节流

throttle-函数节流:一个水龙头在滴水,可能一次性会滴很多滴,但是我们只希望它每隔 500ms 滴一滴水,保持这个频率。即我们希望函数在以一个可以接受的频率重复调用。

function throttle(fn, wait) {
  let previous = 0; // 用于记录上一次执行的时间

  return function() {
    let now = +new Date();
    let remain = wait - (now - previous);

    if (remain < 0) {
      previous = now;
      fn.apply(this, arguments)
    }
  }
}

事件第一次触发的时候我要执行该函数,事件停止后,也要再触发一次函数。

function throttle(fn, wait, options = {}) {
  let previous = 0; // 用于记录上一次执行的时间
  let timeout;

  return function() {
    let now = +new Date();
    let remain = wait - (now - previous);

    if (remain < 0) {
      if (previous === 0 && !options.begin) {
        previous = now;
        return
      }

      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }

      previous = now;
      fn.apply(this, arguments)
    } else if (!timeout && options.end) {
      timeout = setTimeout(() => {
        fn.apply(this, arguments);
        timeout = null;
      }, wait)
    }
  }
}

这样就完成了强大的节流函数了, 第一次来的时候,可以执行一次回调函数,结束的时候也会执行一次回调函数。并且可以通过options来配置。

如果设置了options.begin === true就第一触发事件就立刻会执行回调函数。因为我们设置previous的的初始值为0,所以如果previous === 0就表示是第一次触发该事件,与上options.begin就可以得出第一次是否执行该回调函数。

如果设置了options.end === true事件停止触发后也会执行一次该回调函数。其实在事件触发的整个时间内,定时器中的回调函数从来都没有被执行过,只有在事件停止出发后,定时器内的回调函数才被执行。只要设置了options.end就相当于只是用计算时间的方式来实现节流。