JavaScript 中的防抖和节流

176 阅读2分钟

概念理解

函数基本概念特点
防抖在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时事件触发一段时间之后并且期间没有再次触发才执行
节流在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效稀释函数执行频率

正如参考链接中《7分钟理解JS的节流、防抖及使用场景》一文的比喻:

函数防抖就是法师释放技能的时候要读条,技能读条没完再按技能就会重新读条。

函数节流就是fps游戏的射速,就算一直按着鼠标射击,也只会在规定射速内射出子弹。

应用

防抖

  1. 在输入框中输入内容后再发起请求,而不是每次输入都发起,例如:搜索触发联想词
  2. 浏览器窗口大小改变时会频繁触发 resize 事件,用防抖来让其结束改变时只触发一次

节流

可以将一些事件降低触发频率

  1. 滚动时页面上有些交互效果,此时需要监听scroll事件,但不必每次滑动都触发,可以降低计算的频率,而不必去浪费资源,例如:懒加载时要监听计算滚动条的位置
  2. 商品预览图的放大镜效果时,不必每次鼠标移动都计算位置

代码

理解了这些含义之后,代码写起来会感觉简单许多。

debounce.gif

// 防抖
function debounce(fn, delay=1000) {
  let timer // 定时任务
  return function() {
    const context = this // 谁调用 this 就是谁
    const args = [...arguments] // 谁调用就是谁传入的参数
    if (timer) clearTimeout(timer) // 如果已有则直接取消然后重新设置
    timer = setTimeout(() => {
      fn.apply(this, args) // 通过 apply 将上下文和参数都传进去
    }, delay)
  }
}

throttleByTimeOut.gif

// 节流 定时器版,会计算后再执行,最后结束也会执行一次
function throttleByTimeOut(fn, interval=1000) {
  let timer = null
  return function() {
    const context = this
    const args = [...arguments]
    if (!timer) {
      timer = setTimeout(() => {
        fn.apply(context, args)
        timer = null // 关键操作
      }, interval);
    }
  }
}

throttleByTimeStamp.gif

// 节流 时间戳版,会先执行一次
function throttleByTimeStamp(fn, interval=1000) {
  let prev = new Date().getTime()
  return function() {
    const context = this
    const args = [...arguments]
    let now = new Date().getTime()
    if (now - prev > interval) { // 计算时间间隔
      fn.apply(context, args)
      prev = new Date().getTime() // 关键操作
    }
  }
}

参考链接

  1. 函数防抖和节流
  2. 7分钟理解JS的节流、防抖及使用场景
  3. JS防抖与节流
  4. 防抖和节流的应用场景和实现