节流和防抖


节流和防抖

防抖

防抖的原理就是:你尽管触发事件,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行,总之,就是要等你触发完事件 n 秒内不再触发事件,我才执行。

/**
 * debounce
 * @param {*} fn 要防抖执行的函数
 * @param {*} wait 延迟执行毫秒数
 * @param {*} immediate true 表立即执行,false 表非立即执行
 */
function debounce(fn, wait, immediate) {
  let timer, result;

  let debounced = function () {
    // 将 this 指向正确的对象
    let context = this;
    // 参数
    let args = [...arguments];
    if (timer) {
      clearTimeout(timer);
    }
    if (immediate) {
      // 如果immediate设置为true, 每次调debounce都会走这个分支
      // 第一次执行debounce 或者 wait 时间之后,timer为undifined或null,callNow为true,所以此时会执行一次fn
      // wait时间内再次调用debounce,timer有值所以callNow为false,fn不会执行,直到wait时间之后 timer被改为null
      let callNow = !timer;
      timer = setTimeout(() => {
        timer = null;
      }, wait);
      if (callNow) {
        result = fn.apply(context, args);
      }
    } else {
      timer = setTimeout(() => {
        fn.apply(context, args);
      }, wait);
    }
    // fn的返回值, immediate参数为true的情况下,result才会取到结果
    return result;
  };

  // 取消防抖
  debounced.cancel = function () {
    clearTimeout(timer);
    timer = null;
  };

  return debounced;
}

节流

节流的原理很简单,如果你持续触发事件,每隔一段时间,只执行一次事件。节流会稀释函数的执行频率

使用时间戳

function throttle(fn, wait) {
  let ctx, args;
  let previous = 0;

  return function () {
    let now = Date.now();
    ctx = this;
    args = arguments;
    if (now - previous > wait) {
      fn.apply(ctx, args);
      previous = now;
    }
  };
}

使用定时器

function throttle(fn, wait) {
  let timer, ctx;
  return function () {
    ctx = this;
    args = arguments;
    if (!timer) {
      timer = setTimeout(function () {
        timer = null;
        fn.apply(ctx, args);
      }, wait);
    }
  };
}

underscore 版

function throttle(func, wait, options) {
  let timer, context, args, result;
  let previous = 0;
  if (!options) options = {};

  let later = function () {
    previous = options.leading === false ? 0 : Date.now();
    timer = null;
    func.apply(context, args);
    if (!timer) context = args = null;
  };

  let throttled = function () {
    let now = Date.now();
    if (!previous && options.leading === false) previous = now;
    let remaining = wait - (now - previous);
    context = this;
    args = arguments;
    if (remaining <= 0 || remaining > wait) {
      if (timer) {
        clearTimeout(timer);
        timer = null;
      }
      previous = now;
      func.apply(context, args);
      if (!timer) context = args = null;
    } else if (!timer && options.trailing !== false) {
      timer = setTimeout(later, remaining);
    }
  };

  throttled.cancel = function () {
    clearTimeout(timer);
    previous = 0;
    timer = null;
  };
  return throttled;
}

防抖和节流的区别

  • 防抖是虽然事件持续触发,但只有等事件停止触发后 n 秒才执行函数,节流是持续触发的时候,每 n 秒执行一次函数

  • 函数防抖关注一定时间连续触发的事件只在最后执行一次,而函数节流侧重于一段时间内只执行一次。


 上一篇
JavaScript 实现Promise 及相关API JavaScript 实现Promise 及相关API
/** * 1. new Promise时,需要传递一个 executor 执行器,执行器立刻执行 * 2. executor 接受两个参数,分别是 resolve 和 reject, resolve 和 reject 是 Promis
下一篇 
函数科里化 函数科里化
函数科里化定义函数柯里化又叫部分求值,维基百科中对柯里化 (Currying) 的定义为 柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。 实现func
  目录