不论在日常工作中,还是在面试过程中,防抖节流 总是会时不时的出现在我们的视野里。在日常使用的时候,可能会直接使用第三方库封装好的方法,比如 lodash 中的防抖方法 .debounce 和节流方法 .throttle。抑或是使用开发团队的小伙伴们封装好的方法。

在忙碌的工作中,也许小伙伴们知道它们的作用,但是对其中的原理逐渐陌生。今天我们就把这两个 老朋友 请出来,再来一起看看是如何实现的吧~

节流方法:throttle

什么是节流

用一句话概括节流:

在规定的延时时间内,不管你执行多少次,我都只认第一次。

节流的实现

/**
 * 节流函数
 * @param fn
 * @param delay
 */
function throttle (fn, delay) {
 // 上一次的调用时间
 let lastTime = 0

 // 将throttle结果以函数形式返回
 return function () {
  // 保存执行时上下文
  const context = this
  // 保存调用throttle结果函数时传入的参数
  const args = arguments
  // 本地调用的时间
  const time  = new Date()
  // 如果本次调用时间减去上一次的调用时间大于延迟时间,则触发传入的fn
  if (time - lastTime > delay) {
   lastTime = time
   fn.apply(context, args)
  }
 }
}

防抖方法:debounce

什么是防抖

用一句话概括防抖:

在规定的延时时间内,不管你触发多少次回调,都只认最后一次。

防抖的实现

/**
 * 防抖
 * @param fn
 * @param delay
 */
function debounce (fn, delay) {
 // 保存延时
 let timer = null
 // 将debounce的处理结果以函数形式返回
 return function () {
  // 保存执行时上下文
  const context = this
  // 保存执行时传入的参数
  const args = arguments
  // 如果存在定时器,则将其清除
  timer && clearTimeout(timer)
  // 设置新的定时器,在计时结束后触发回调
  timer = setTimeout(() => {
   fn.apply(context, args)
  }, delay)
 }
}

防抖的弊端

防抖与节流的目的,都是在一定时间内限制回调函数被调用的次数,从而在一定程度上提升性能。

但是防抖存在一个弊端,那就是如果在 delay 指定的时间内不断触发防抖,就会导致回调函数迟迟不会有响应。只有在你 停手 之后,经过 dealy 这段时间后,回调函数才会有响应。

既然如此,如果我们能在一开始就触发回调,然后在后续频繁的操作里保持防抖的特性,岂不是美滋滋~

image.png

加强版的节流方法:strongerThrottle

回顾我们之前所实现的 节流 方法,它不正是在一开始就可以立即调用回调函数的么?既然如此,我们可以使用 throttle 方法来对 debounce 方法进行优化,实现一个加强版的节流方法。

/**
 * 更健壮的节流,结合了防抖
 * @param fn
 * @param delay
 */
function strongerThrottle (fn, delay) {
 // 保存计时器与最后一次调用时间
 let timer = null
 let lastTime = 0
 // 将调用strongerThrottle的结果以函数形式返回
 return function () {
  // 保存执行上下文
  const context = this
  // 保存传入的参数
  const args = arguments
  // 保存当前调用时间
  const time = new Date()
  // 如果当前调用的时间减去最后一次调用的时间小于延迟时间,则设置定时器
  if (time - lastTime < delay) {
   // 如果存在定时器,则先清空
   timer && clearTimeout(timer)
   // 设置新的定时器,在计时结束后触发回调
   timer = setTimeout(function () {
    lastTime = time
    fn.apply(context, args)
   }, delay)
  } else {
   // 如果超出时间,则给出响应
   lastTime = time
   fn.apply(context, args)
  }
 }
}