一文搞懂防抖与节流

166 阅读3分钟

一、防抖

当事件触发时,延迟一段时间后才能再次执行事件处理函数,如果这段时间内事件被重新触发,那么重新开始计时

1、防抖函数的实现原理

  • 当事件第一次触发时,设置一个定时器,延迟指定的时间后执行处理函数。
  • 如果在延迟时间内事件再次被触发,则清除之前的定时器,重新设置一个新的定时器。
  • 只有当事件在延迟时间内没有再次被触发时,处理函数才会执行。

防抖函数是闭包的应用,看一个防抖函数的实现例子

不了解闭包的话,跳转到另一篇文章学习一下深入理解 JavaScript 中的上下文、作用域与闭包

function debounce(fun,delay){
    let timer = null
    return function(...args){
        if(timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            fun.apply(this,args)
        },delay)
    }
}

const handleClick = debounce(() => {
    console.log("被点击了")
},300)

btn.addEventListener('click', handleClick);

2、事件触发时函数的执行情况

假设按钮连续点击:

第一次触发 click 事件:

  1. handleClick(...args) 被调用
  2. timer 为 null,跳过 clearTimeout
  3. 设置新的定时器:timer = setTimeout(..., 300)
  4. 300ms 后执行:fun.apply(this, args) → 输出"被点击了"

在 300ms 内第二次触发:

  1. handleClick(...args) 再次被调用
  2. timer 有值,执行 clearTimeout(timer) → 取消之前的定时器
  3. 设置新的定时器:timer = setTimeout(..., 300)
  4. 重新开始 300ms 计时

在 300ms 内第三次触发:

  1. 重复上述过程,再次取消并重新计时

最后一次触发后 300ms 内没有新事件:

  1. 定时器到期,执行 fun.apply(this, args)
  2. 输出"被点击了"

3、fun.apply(this,args),为什么必须要绑定this

涉及到了this绑定,不了解的点击一文搞懂call,apply,bind三者区别、实现原理以及应用场景学习

在 JavaScript 中,函数的 this 值由调用方式决定,而不是定义位置。

handleClickthis是指向btn的,因为它是由btn调用的,但是fun是直接被使用的,如果不使用 applyfun 在被直接调用时,其 this 值不会自动绑定到事件处理函数的 this(即 btn),而是使用默认绑定(严格模式下为 undefined)也就是undefined.fun()。使用 fun.apply(this, args) 是为了显式地将外层函数的 this 和参数传递给 fun,使得fun能够正确获取DOM元素上的属性和方法。

二、节流

节流的思想是:在一定时间内只允许事件处理函数执行一次,无论事件触发了多少次。

1、节流函数的实现原理

  • 设置一个时间间隔(例如100ms)。
  • 当事件第一次触发时,立即执行处理函数,并记录当前时间。
  • 在后续事件触发时,检查当前时间与上次执行时间的间隔是否大于设定的时间间隔,如果是,则再次执行处理函数并更新时间记录。

同样的是闭包的应用

function throttle(fun,limit){
    let lastTime = null
    return function(...args){
        let nowTime = new Date().getTime()
        if(nowTime - lastTime > limit) {
            func.apply(this, args)
            lastTime = now
        }
    }
}

const handleScroll = throttle(() => { console.log('页面滚动了') }, 100)

window.addEventListener('scroll', handleScroll)

三、防抖与节流的区别

  • 防抖确保事件在最后一次触发时才执行处理函数,适用于需要在事件结束后才执行的场景,减少不必要的操作和网络请求,提高性能,但是也会因延迟操作,会导致有些用户感知上的延迟。
  • 节流在固定的时间间隔内只执行一次处理函数。适合那些需要在事件频繁触发时控制执行频率的场景(如鼠标移动、滚动事件)。可以保证响应频率的同时减少函数的执行次数,提高性能。