手写防抖(debounce)和节流(throttle)函数

防抖(Debounce)

事件停止触发 n 秒后执行,如果在 n 秒内再次触发则重新计时。

function debounce(fn, delay, immediate = false) {
  let timer = null

  return function (...args) {
    if (timer) clearTimeout(timer)

    if (immediate && !timer) {
      fn.apply(this, args)
    }

    timer = setTimeout(() => {
      if (!immediate) fn.apply(this, args)
      timer = null
    }, delay)
  }
}

// 使用
input.addEventListener('input', debounce(search, 300))
window.addEventListener('resize', debounce(updateLayout, 200))

应用场景:搜索框输入、窗口 resize、表单验证

节流(Throttle)

每 n 秒最多执行一次,在此期间的触发被忽略。

// 时间戳版(第一次立即执行,最后一次不执行)
function throttle(fn, interval) {
  let lastTime = 0
  return function (...args) {
    const now = Date.now()
    if (now - lastTime >= interval) {
      lastTime = now
      fn.apply(this, args)
    }
  }
}

// 定时器版(第一次不执行,最后一次会执行)
function throttle(fn, interval) {
  let timer = null
  return function (...args) {
    if (!timer) {
      timer = setTimeout(() => {
        fn.apply(this, args)
        timer = null
      }, interval)
    }
  }
}

// 组合版(第一次和最后一次都执行)
function throttle(fn, interval) {
  let lastTime = 0
  let timer = null
  return function (...args) {
    const now = Date.now()
    const remaining = interval - (now - lastTime)

    if (remaining <= 0) {
      if (timer) { clearTimeout(timer); timer = null }
      lastTime = now
      fn.apply(this, args)
    } else if (!timer) {
      timer = setTimeout(() => {
        lastTime = Date.now()
        timer = null
        fn.apply(this, args)
      }, remaining)
    }
  }
}

应用场景:滚动加载、按钮防重复点击、鼠标拖拽

对比

防抖 节流
原理 重置计时器 固定频率执行
类比 电梯等人 电梯定时发车
场景 搜索联想 滚动监听

同分类其他题目