防抖节流

145 阅读5分钟

防抖 Debounce

  • 防抖(Debounce)是一种常用的前端优化技术,用于限制在事件频繁触发时执行回调函数的次数。当一个事件被触发后,如果在一定的时间间隔内又被触发,那么只有最后一次触发的事件会执行回调函数,而之前的事件将被忽略。
  • 防抖的主要思想是延迟执行回调函数,在每次触发事件后设置一个定时器,在指定的时间间隔内没有再次触发事件时,才执行回调函数。如果在该时间间隔内又触发了事件,则重新设置定时器,取消之前的定时器。

防抖的应用场景包括但不限于:

  • 输入框搜索联想:在用户连续输入时,避免频繁地触发搜索请求,而是在用户停止输入一段时间后再发起搜索请求。
  • 窗口大小调整:在窗口大小调整过程中,避免频繁地更新布局,而是在用户停止调整窗口大小后再执行布局更新操作。
  • 按钮点击防重复:在按钮被快速点击多次时,避免多次提交请求或执行操作,而是只响应最后一次点击。

防抖函数精简版:

function debounce(func, delay) {
  let timeoutId;

  return function(...args) {
    clearTimeout(timeoutId);

    timeoutId = setTimeout(() => {
      func.apply(this, args);
    }, delay);
  };
}

// 示例回调函数
function callback() {
  console.log('Debounced function is called');
}

// 使用防抖包装回调函数
const debouncedFn = debounce(callback, 300);

// 在需要防抖的事件上绑定防抖函数
document.addEventListener('scroll', debouncedFn);

  1. 在上面的示例中,我们定义了一个防抖函数 debounce,它接受两个参数:func 是需要进行防抖的回调函数,delay 是延迟时间(以毫秒为单位)。
  2. 在返回的函数中,我们使用 setTimeout 来延迟执行回调函数 func。如果在延迟期间再次调用了返回的函数,我们会清除之前的定时器,重新设置新的定时器,确保在最后一次调用后的一段时间内没有新的调用才执行回调函数。
  3. 在示例中,我们定义了一个回调函数 callback,它在防抖后被调用时会输出一条消息。
  4. 通过使用防抖函数,我们将回调函数 callback 使用 debounce 包装,并将得到的防抖函数赋值给变量 debouncedFn。然后,我们在需要进行防抖的事件上(此处为 scroll 事件)绑定防抖函数 debouncedFn
  5. 这样,当滚动事件触发时,防抖函数会被调用,但实际上只有在停止滚动一段时间后才会执行回调函数。这样可以避免在频繁滚动时频繁触发回调函数,提升性能并避免不必要的操作。

节流 Throttling

  • 节流(Throttling)是一种限制函数执行频率的技术。它确保在一定时间间隔内,函数只能执行一次。
  • 节流的基本思想是,当触发事件时,如果在设定的时间间隔内函数已经被调用过了,则忽略该次触发,直到过了设定的时间间隔后,函数才会再次执行。
  • 节流的主要作用是减少函数的执行次数,特别是在高频率触发的事件中,如滚动、窗口调整等。通过控制函数的执行频率,可以降低事件处理的资源消耗,提升性能,并且避免在短时间内多次执行函数导致的不必要的操作。

示例:

下面的代码实现了一个基本的节流函数 throttle

const throttle = (fn, ms = 0) => {
                let start = 0
                return function(...args){
                let now = Date.now()
                if (now - start >= ms){
                    fn.apply(this, args)
                    start = Date.now()
                }
            }
        }
  1. 在节流函数内部,定义了一个 start 变量,初始值为 0。该变量用于记录上一次函数执行的时间戳。
  2. 返回的函数使用了剩余参数语法 ...args,以便可以接收任意数量的参数。
  3. 在返回的函数内部,首先获取当前的时间戳,使用 Date.now() 方法可以得到当前时间距离 1970 年 1 月 1 日的毫秒数。
  4. 接下来,计算当前时间戳 now 与上一次函数执行的时间戳 start 的时间差。如果时间差大于等于设定的时间间隔 ms,则执行函数。
  5. 接下来,计算当前时间戳 now 与上一次函数执行的时间戳 start 的时间差。如果时间差大于等于设定的时间间隔 ms,则执行函数。

定时器版本:

       const throttle = (fn, ms = 0) => {
            let timerId  
            return function (...args) {
                if (timerId) return
                timerId = setTimeout(() => {
                    fn.call(this, ...args)
                    timerId = null
                }, ms);
            }
        }
  1. 在节流函数中,我们定义了一个 timerId 变量,并将其初始值设置为 undefined
  2. 返回的函数使用了剩余参数语法 ...args,以便可以接收任意数量的参数
  3. 在返回的函数内部,首先检查 timerId 的值。如果存在值(即不为 undefined),表示已经设置了定时器并处于等待执行状态,直接返回,不再开启新的定时器。这是为了避免在时间间隔内连续调用时重复触发函数的执行
  4. 如果 timerIdundefined,表示定时器未设置,可以执行函数。于是,我们使用 setTimeout 开启一个定时器,延迟执行函数。在定时器回调函数中,我们调用原始的函数 fn,使用 call 方法传递正确的上下文(this)和参数(...args)。然后,将 timerId 设置为 null,表示定时器已执行完毕。
  • 通过这样的实现,我们实现了一个基本的节流函数。它能确保在设定的时间间隔内只执行一次函数,避免过于频繁的调用。当连续调用时,只有在时间间隔经过后才会再次执行函数。

lodash实现节流防抖

  • 最简单的方式
<div class="box"></div>
<script src="./js/lodash.min.js"></script>
<script>
    const box = document.querySelector('.box')
    let i = 0

    const move = (x, y) => {
        i++
        box.innerHTML = i
    }

    // box.addEventListener('mousemove', move)
    box.addEventListener('mousemove', _.debounce(move, 300))
    box.addEventListener('mousemove', _.throttle(move, 300))
</script>
  1. 去lodash网址下载js文件
  2. 引入文档
  3. _.debounce(move, 300) 或 _.throttle(move, 300)