Skip to content

防抖与节流

作者:guo-zi-xin
更新于:9 个月前
字数统计:875 字
阅读时长:3 分钟

防抖与节流本质上是优化高频率代码的一种手段, 如浏览器的 resizescrollkeypressmousemove等事件在触发时,会不断调用绑定在事件上的回调函数, 极大的浪费资源,降低页面性能

为了优化体验,需要对这类事件进行调用次数限制,对此我们就可以采用 *防抖(debounce)*和 *节流(throttle)*的方式来减少调用频率

节流(throttle)

定义

节流是指程序在n秒内只运行一次,若在这个时间段内重复触发,则只生效一次

节流实现方式

声明一个变量当标志位,记录当前代码是否在执行,如果正在执行,取消这次方法的执行,直接 retun, 如果有空闲,正常触发方法执行

  • 时间戳写法

    事件会立即执行,停止触发后没有办法再次执行

javascript
const throttleDate = (fn, delay = 500) => {
  let oldtTme = Date.now()
  return function (...args) {
    let newTime = Date.now()
    if (newTime - oldTime >= delay) {
      fn.apply(null, args)
      oldTime = Date.now()
    }
  } 
}
  • 定时器写法

    delay 毫秒后第一次执行,第二次事件停止触发后依然会再一次执行

javascript
const throttleInterval = (fn, delay = 500) => {
  let timer = null
  return function (...args) {
    if (timer) {
      timer = setTimeout(() => {
        fn.apply(this, args)
        timer = null
      }, delay)
    }
  } 
}
  • 两者结合
javascript
const throttled = (fn, delay) => {
  let timer = null
  let startTime = Date.now()
  return function () {
    let curTime = Date.now() // 当前时间
    let remaining = delay - (curTime - startTime) // 从上一次到现在,还剩下多少剩余时间
    let context = this
    let args = arguments
    clearTimeout(timer)
    if (remaining <= 0) {
      fn.apply(context, args)
      startTime = Date.now()
    } else {
      timer = setTimeout(fn, remaining)
    }
  }
}

防抖(debounce)

防抖是指要在n秒后开始执行程序, 若在这个时间段内被重复触发,则重新开始计时

防抖实现方式

需要一个延时定时器来辅助实现,延迟执行需要执行的代码,如果方法多次触发,把上次记录的延迟执行代码用 clearTimeout 清除掉, 重新开始计时, 如果计时完毕,没有方法来访问触发, 则执行代码

javascript
const debounce = (func, wait) => {
 let timeout;
 return function () {
  let context = this // 保存this指向
  let args = arguments; // 参数对象
  clearTimeout(timeout)
  timeout = setTimeout(()=> {
    fn.apply(context, args)
  }, wait)
 }
}
  • 立即执行版本
javascript
const debounce = (func, wait, immediate) => {
  let timeout;
  return function () {
    let context = this
    let args = arguments
    
    if (timeout) clearTimeout(timeout); // timeout 不为null

    if (immediate) {
      let callNow = !timeout // 第一次会立即执行,以后只有事件执行后才会再次触发
      timeout = setTimeout(() => {
        timeout = null
      }, wait)

      if (callNow) {
        func.apply(context, args)
      }
    } else {
      timeout = setTimeout(() => {
        func.apply(context, args)
      }, wait)
    }
  }
}

区别

相同点

  • 都可以通过 setTimeout 实现
  • 目的都是,降低回调执行频率,节省计算资源

差异

  • 防抖是在一段连续操作结束后,处理回调,利用clearTimeoutsetTimeout 来实现;而节流是在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能
  • 防抖关注一定事件内连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次

应用场景

  • 防抖

    • 搜索框搜索输入,只需要用户最后一次输入完,再发送请求
    • 手机号、邮箱验证输入检测
    • 窗口大小resize, 只需要在窗口完成调整后,再计算窗口大小,防止重复渲染
  • 节流

    • 滚动加载,加载更多或滚到底部监听
    • 搜索框的搜索联想功能

来源

防抖与节流
https://vue3js.cn/interview/JavaScript/debounce_throttle.html#%E4%BA%8C%E3%80%81%E5%8C%BA%E5%88%AB

人生没有捷径,就像到二仙桥必须要走成华大道。