JavaScript 防抖与节流

防抖:多次被触发执行只会执行延迟执行最后一次

节流:每隔一段时间执行一次,或者说一段时间内只执行一次

防抖

function debounce(fn, delay = 300) {
  let timer = null;
  return typeof fn === "function"
    ? e => {
        timer && clearInterval(timer);
        timer = setTimeout(_ => {
          fn(e);
          clearInterval(timer);
          timer = null;
        }, delay);
      }
    : _ => {};
}

节流

function throttle(fn, delay = 300) {
  let timer = null;
  return typeof fn === "function"
    ? e => {
        if (!timer) {
          timer = setTimeout(_ => {
            fn(e);
            clearInterval(timer);
            timer = null;
          }, delay);
        }
      }
    : _ => {};
}

防抖可用于页面的搜索框,滚动,窗口大小变化时需要执行处理函数的场景

节流可用的场景目前我还没想到。没想到不代表没用,只是还没遇到这样的场景

上面两种方法第一次触发时不是实时执行,而是延时执行的。不过有时我们需要第一次实时执行,后面的才延时执行。

防抖

function debounce(fn, delay = 300) {
  let timer = null;
  let handlerType = 1; // 是否是首次处理
  let handler = {
    0: e => {
      timer && clearInterval(timer);
      timer = setTimeout(_ => {
        fn(e);
        handlerType = 1;
        clearInterval(timer);
        timer = null;
      }, delay);
    },
    1: e => {
      handlerType = 0;
      fn(e);
    }
  };
  return typeof fn === "function"
    ? e => {
        handler[handlerType](e);
      }
    : _ => {};
}

节流

function throttle(fn, delay = 300, options = {}) {
  let timer = null;
  let switchTimer = null;
  let immediately = options.immediately || 1; // 是否是首次立刻执行
  const wait = options.wait || 100; // 多长时间后 throttle 恢复初始状态(先立即执行一次)
  const handler = {
    0: e => {
      if (!timer) {
        // 如果已存在要执行的任务,则忽略后面的
        if (switchTimer) {
          clearInterval(switchTimer);
          switchTimer = null;
        }
        timer = setTimeout(_ => {
          fn(e); // 执行回调
          timer = null;
          clearInterval(timer);
          switchTimer = setTimeout(_ => {
            immediately = 1;
          }, wait);
        }, delay);
      }
    },
    1: e => {
      immediately = 0;
      fn(e);
    }
  };
  return typeof fn === "function"
    ? e => {
        handler[immediately](e);
      }
    : _ => {};
}

节流配置版

function throttle( fn, wait = 300, options = {} ) {
  // options{ immediately, delay },immediately:是否是首次立刻执行,delay:是否延时执行最后一次
  let timer = null;
  let now = 0;
  let last = 0;
  let remain = 0;
  const handler = e => {
	  now = Date.now();
	  !options.immediately && !last && (last = now-wait);
	  remain = now - last;
  	  // 要存在,不管有没执行完,先清掉定时器
	  if(timer){
	  	clearInterval(timer);
	  	timer = null;
	  }
      // 如果每次触发不需要立即执行或者已经到了时间点
      if(!last || wait - remain <= 0){
      	fn(e); // 执行回调
      	last = Date.now(); // 记录此次执行完的时间点
      }
      if(options.delay){ // 如果设置了最后一次延时执行,则通过定时器延时执行最后一次
        timer = setTimeout(_ => {
          fn(e); // 执行回调
          timer = null;
          clearInterval(timer);
        }, wait);
      }
  };
  return handler;
}

去掉了 fn 参数的判断