菜鸟进阶之路:一文讲清JS的防抖和节流,心疼一下可怜的回调函数

31 阅读3分钟

前言

在日常开发中经常会遇到这样的应用场景:需要判断滚轮是否滚到最底部以进行滚动加载,处理输入框中的输入内容,以及监听浏览器窗口大小等操作。这些操作通常会频繁连续地触发相应的回调函数,从而浪费资源并降低前端性能。

为了优化体验、降低资源消耗、提升性能,也为了防止被测试同学暴捶,我们还是还是有必要在日常开发中注意使用防抖和节流的。

image.png

介绍

首先,让我们来讨论一下防抖和节流的概念:

在之前提到的高频率触发回调函数的情况下,为了限制这些事件的调用次数,我们可以采用防抖(debounce)和节流(throttle)的方法来降低调用频率。

定义:

  • 节流:在n秒内只执行一次,若在n秒内多次触发,则只有一次生效。
  • 防抖:n秒后执行该事件,若在n秒内再次触发,则重新计时。

一个经典的比喻是想象每天上班时在大厦底下等候电梯。将电梯完成一次运送比作为一次函数的执行和响应。

假设电梯有两种运行策略:debouncethrottle,超时设定为15秒,不考虑容量限制。

  • 当第一个人进入电梯后,15秒后准时运送一次,这就是节流。
  • 当第一个人进入电梯后,等待15秒。如果期间有更多人进入,15秒等待时间会重新计时,直到15秒后开始运送,这就是防抖。

代码实现参考

防抖

function 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);
        }
    }
}

节流

function 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(function () {
                timeout = null;
            }, wait)
            if (callNow) {
                func.apply(context, args)
            }
        }
        else {
            timeout = setTimeout(function () {
                func.apply(context, args)
            }, wait);
        }
    }
}

区别

相同点:

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

不同点:

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

例如,若将时间频率设置为500ms,在2秒内频繁触发函数时,节流会每隔500ms执行一次。而防抖则在2秒后,无论调用多少次方法,只会执行一次。

应用场景

防抖适用于连续事件中只需触发一次回调的场景,例如:

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

节流适用于间隔一段时间执行一次回调的场景,例如:

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

结语

至此,JS的防抖和节流就已经介绍完了。

欢迎大家留言讨论,不足之处也烦请批评指正。