防抖和节流

45 阅读2分钟

节流和防抖都是为了防止某一时间段内事件回调函数频繁触发

防抖 debounce

概念:函数在触发n时间后才执行,如果在时间n内再次触发函数,则n重新计算,可以这么理解:debounce就是法师读条释放技能,如果被打断,就要重新读条

使用场景:

  • 按钮的防重复点击
  • search搜索联想,用户在不断输入值时,用防抖来节约请求资源
  • window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次

简单实现

function debounce(fn, delay) {
  let timer = null
  return function(...args) {
    if(timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => fn.apply(this, args), delay)
  }
}

业务需求中可能需要第一次立即执行的场景

function debounce(fn, delay, immediate) {
  let timer = null
  return function(...args) {
    if(immediate) {
      // 第一次执行后取反
      immediate = !immediate
      fn.apply(this, args)
    } else {
      if(timer) {
        clearTimeout(timer)
      }
      timer = setTimeout(() => fn.apply(this, args), delay)
    }
  }
}

const clickFn = debounce((arg) => console.log(arg), 1000, true)

const btn = document.getElementById('btn')
btn.addEventListener('click', () => {
  clickFn('do some thing')
})

节流 throttle

概念:在n时间内,无论触发多少次,函数最多只会执行一次,可以这么理解:类似fps的射击游戏,无论你点击鼠标多频繁,子弹只会按照固定的射速发射

使用场景:

  • 鼠标不断点击触发(单位时间内只触发一次)
  • 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断

简单实现(立即执行)

// 定时器实现
function throttle (fn, delay) {
  let isRunning = false // 状态位
  return function(...args) {
    if (!isRunning) {
      isRunning = true
      fn.apply(this, args)
      setTimeout(() => {
        isRunning = false
      }, delay)
    }
  }
}

节流函数不止上面一种实现方式,可以使用时间戳来代替状态位来判断时间

时间戳实现

function throttle(fn, delay) {
  let lastCall = 0
  return function(...args) {
    const now = new Date().getTime()
    if (now - lastCall > delay) {
      fn.apply(this, args)
      lastCall = now
    }
  }
}