函数防抖与节流算是前端性能优化的一个细节点,可能开发大多数是用着 JS 库顺带的防抖节流函数,但我们必须会自己写因为没有必要因为一个函数引入一个库。
防抖与节流效果
这是别人做的一个动画,很容易就能理解区别:防抖与节流
函数防抖(debounce)
在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   | function debounce(fn, wait = 50, immediate = false) {   let timer = null
    function debounced(...args) {          const context = this          if (timer) clearTimeout(timer)
                if (immediate && !timer) {       fn.apply(context, args)     }
      timer = setTimeout(() => {       fn.apply(context, args)     }, wait)   }
    debounced.cancel = function cancel() {          clearTimeout(timer)     timer = null   }
    return debounced }
  | 
应用场景
适合多次事件一次响应的情况
- search 搜索联想,用户在不断输入值进行 AJAX 验证时时,用防抖来节约请求资源。
 - 按钮点击事件/input 事件,防止用户多次重复提交
 - 电话号码输入的验证, 只需停止输入后进行一次。
 
函数节流(throttle)
规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
定时器版
1 2 3 4 5 6 7 8 9 10 11 12
   | function throttle(func, wait) {   let timeout = null   return function throttled(...args) {     const ctx = this     if (!timeout) {       timeout = setTimeout(() => {         func.apply(ctx, args)         timeout = null       }, wait)     }   } }
  | 
时间戳节流
1 2 3 4 5 6 7 8 9 10 11 12
   | 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 画笔功能
 - 游戏中的刷新率