Skip to content

Latest commit

 

History

History
99 lines (79 loc) · 2.9 KB

手写防抖与节流函数.md

File metadata and controls

99 lines (79 loc) · 2.9 KB

手写防抖与节流函数

前言

函数防抖与节流算是前端性能优化的一个细节点,可能开发大多数是用着 JS 库顺带的防抖节流函数,但我们必须会自己写因为没有必要因为一个函数引入一个库。

防抖与节流效果

这是别人做的一个动画,很容易就能理解区别:防抖与节流

函数防抖(debounce)

在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。

function debounce(func, wait, immediate) {
  let timeout
  function debounced(...args) {
    const ctx = this
    if (timeout) clearTimeout(timeout) // 每次调用debounce函数都会将前一次的timer清空,确保只执行一次

    if (immediate) {
      const callNow = !timeout
      timeout = setTimeout(() => {
        // 这里只有在 wait 时长之后,timeout 为 null,然后触发防抖函数才能立即执行,
        // 否则 callNow 为 false,不会立即执行,计时器会重新计时
        timeout = null
      }, wait)
      if (callNow) func.apply(ctx, args)
    } else {
      timeout = setTimeout(() => {
        func.apply(ctx, args)
      }, wait)
    }
  }
  debounced.cancel = function cancel() {
    // 取消功能
    clearTimeout(timeout)
    timeout = null
  }
  return debounced
}

应用场景

适合多次事件一次响应的情况

  • search 搜索联想,用户在不断输入值进行 AJAX 验证时时,用防抖来节约请求资源。
  • 按钮点击事件/input 事件,防止用户多次重复提交
  • 电话号码输入的验证, 只需停止输入后进行一次。

函数节流(throttle)

规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。

定时器版

function throttle(func, wait) {
  let timeout = 0
  return function throttled(...args) {
    const ctx = this
    if (!timeout) {
      timeout = setTimeout(() => {
        func.apply(ctx, args)
        timeout = null
      }, wait)
    }
  }
}

时间戳节流

function throttle(func, wait) {
  let previous = 0
  return function throttled(...args) {
    const ctx = this
    const now = Date.now()
    const remain = wait - (now - previous)
    if (remain <= 0) {
      func.apply(ctx, args)
      previous = now
    }
  }
}

应用场景

适合大量事件按时间做平均分配触发。

  • 监听滚动或 resize 事件,比如是否滑到底部自动加载更多,调整窗口大小用 throttle 来判断
  • DOM 元素拖拽
  • 自动保存草稿功能,当用户在输入的时候(一直触发事件),单位时间内只保存一次草稿
  • Canvas 画笔功能
  • 游戏中的刷新率