防抖和节流,是常用的用于性能优化的手段,降低回调函数的执行频率,节省计算资源,防止页面出现阻塞卡顿现象。

防抖

简单理解防抖的概念,当一个动作频繁被触发,但是只执行一次。
其实从名字上去理解,它就是 “防止手抖”
实现的核心就是:方法被触发时设定一个时间去判断,如果在等待时间又被触发了,就重新开始计时,一直到事件结束才去触发执行动作。

代码实现

function debounce(fn, delay = 50) {
  let timer = null;

  return (...args) => {
    if(timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.call(this, ...args);
    }, delay);
  }
}

上面这个实现,是利用 setTimeout 延迟执行方法,并将其赋值给 timer 变量保存,如果是再次被触发则清楚上次的延时,重新开始计算时间。
这种实现最终是只执行多次触发后的最后一次,所以第一次不会实际执行,如果有需求是要立刻执行的,就需要改造一下:

function debounce(fn, delay = 50, immediate = true) {
  let timer = null;

  return (...args) => {
    if (timer) clearTimeout(timer);
    immediate && !timer && fn.call(this, ...args);
    timer = setTimeout(() => {
      timer = null;
      !immediate && fn.call(this, ...args);
    }, delay);
  }
}

最终版通过参数 immediate 判断设么时候执行,当它为 true 的时候,第一次执行 timer 肯定是 false ,所以 fn 会立即执行,然后通过 setTimeout 延时将变量 timer 赋值成 null ,如果在延时时间内再次点击时因为 timer 不为 null 所以不会执行函数。

使用场景

  • 按钮点击,避免用户点击过快,导致多次后向后端发送请求。
  • 浏览器窗口调整时,使用防抖防止 resize 多次计算
  • 获取输入框值得时候,使用防抖在用户输入完后才去获取最终的值

节流

节流就是在一定时间间隔中指触发一次事件,打个比方就像是游戏中的技能的冷却,当技能还在冷却时间的时候键盘摁爆也不会释放技能,只有冷却时间结束了才能使用,然后使用完又开始冷却等待。

代码实现

节流函数可以通过时间戳和定时器来实现

  • 时间戳
function throttle(fn, delay) {
  let prev = 0;

  return (...args) => {
    const now = new Date();
    if(now - prev > delay) {
      fn.call(this, ...args);
      prev = now;
    }
  }
}
  • 定时器
function throttle(fn, delay) {
  let timer = null;

  return (...args) => {
    if(!timer) {
      timer = setTimeout(() => {
        clearTimeout(timer);
        timer = null;
        fn.call(this, ...args);
      }, delay);
    }
  }
}

使用场景

  • 监听滚动事件,隔一段时间再次去处理
  • input 框实时搜索请求显示下拉列表情况