• 防抖debounce与节流throttle(63rd)


    一、前言

    当用户高频触发某一事件时,如窗口的resize、scroll,输入框内容校验等,此时这些事件调用函数的频率如果没有限制,可能会导致响应跟不上触发,出现页面卡顿,假死现象。此时,我们可以采用防抖(debounce) 和 节流(throttle) 的方式来减少调用频率,同时又不影响实际效果。

    本质上是优化高频率执行代码的一种手段

    二、防抖

    防抖:就是指触发事件后,函数在 n 秒后只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数的执行时间。

    简单的说,当一个函数连续触发,只执行最后一次。

    案例理解

    防抖的场景:小谢催小尹还钱,小尹决定三天后还小谢的钱,但是小谢催款的话,小尹就重新计算这个三天的期限,20号催款,23还,21号再催,就24号还。除非小谢这三天没有催小尹还钱。

    函数防抖一般用在什么情况之下呢?一般用在,连续的事件只需触发一次回调的场合。具体有:

    1、搜索框搜索输入。只需用户最后一次输入完,再发送请求;
    2、用户名、手机号、邮箱输入验证;
    3、浏览器窗口大小改变后,只需窗口调整完后,再执行resize事件中的代码,防止重复渲染。

    代码实现

    在下面这段代码中,我们实现了最简单的一个防抖函数,我们设置一个定时器,你重复调用一次函数,我们就清除定时器,重新定时,直到在设定的时间段内没有重复调用函数。

    // fn是你要调用的函数,delay是防抖的时间
    function debounce(fn, delay) {
      // timer是一个定时器
      let timer = null;
      // 返回一个闭包函数,用闭包保存timer确保其不会销毁,重复点击会清理上一次的定时器
      return function () {
        // 调用一次就清除上一次的定时器
        clearTimeout(timer);
        // 开启这一次的定时器
        timer = setTimeout(() => {
          fn();
        }, delay)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    代码优化

    仔细一想,上面的代码是不是有什么问题?

    问题一: 我们返回的fn函数,如果需要事件参数e怎么办?事件参数被debounce函数保存着,如果不把事件参数给闭包函数,若fn函数需要e我们没给,代码毫无疑问会报错。

    问题二: 我们怎么确保调用fn函数的对象是我们想要的对象?你发现了吗,在上面这段代码中fn()函数的调用者是fn所定义的环境,这里涉及this指向问题,想要了解为什么可以去了解下js中的this。
    为了解决上述两个问题,我们对代码优化如下

    // fn是你要调用的函数,delay是防抖的时间
    function debounce(fn, delay) {
      // timer是一个定时器
      let timer = null;
      // 返回一个闭包函数,用闭包保存timer确保其不会销毁,重复点击会清理上一次的定时器
      return function () {
        // 保存事件参数,防止fn函数需要事件参数里的数据
        let arg = arguments;
        // 调用一次就清除上一次的定时器
        clearTimeout(timer);
        // 开启这一次的定时器
        timer = setTimeout(() => {
          // 若不改变this指向,则会指向fn定义环境
          fn.apply(this, arg);
        }, delay)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    三、节流

    节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效

    简单来说节流就是限制一个函数在一段时间内只能执行一次,过了这段时间,在下一段时间又可以执行一次。

    案例理解
    节流的场景:小尹催小谢还钱,小谢决定三天内还小尹的钱,就算小尹这三天怎么催,小谢都会在第一次听到要还钱后的第三天,把钱还了。20号第一次催款,不论21,22号有没有催,小谢都会在23号把钱还给小尹。

    应用场景如:

    1、输入框的联想,可以限定用户在输入时,只在每两秒钟响应一次联想。
    2、搜索框输入查询,如果用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户停止输入的时候,再调用,设
    置一个合适的时间间隔,有效减轻服务端压力。
    3、表单验证
    4、按钮提交事件。

    代码实现

    // 时间戳 & 定时器方法
    function throttle(fn, delay) {
      // 初始化定时器
      let timer = null;
      // 上一次调用时间
      let prev = null;
      // 返回闭包函数
      return function () {
        // 现在触发事件时间
        let now = Date.now();
        // 触发间隔是否大于delay
        let remaining = delay - (now - prev);
        // 保存事件参数
        const args = arguments;
        // 清除定时器
        clearTimeout(timer);
        // 如果间隔时间满足delay
        if (remaining <= 0) {
          // 调用fn,并且将现在的时间设置为上一次执行时间
          fn.apply(this, args);
          prev = Date.now();
        } else {
          // 否则,过了剩余时间执行最后一次fn
          timer = setTimeout(() => {
            fn.apply(this, args)
          }, delay);
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    四、区别

    相同点:

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

    不同点:

    1、函数防抖,在一段连续操作结束后,处理回调,利用clearTimeout和 setTimeout实现。函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能

    2、函数防抖关注一定时间连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次

    例如,都设置时间频率为500ms,在2秒时间内,频繁触发函数,节流,每隔 500ms 就执行一次。防抖,则不管调动多少次方法,在2s后,只会执行一次
    在这里插入图片描述

  • 相关阅读:
    毕业答辩PPT怎么做?制作PPT必备的模板网站和AI工具来了!
    Dubbo:Nacos作为注册中心
    国产压力测试工具的主要作用
    工程师必须记住的电路元件符号及英语翻译
    AutoJs学习-趣玩版跳一跳相关代码
    C++opencv 色彩空间转换和保存
    探索GitHub上的两个革命性开源项目
    TikTok风控问题和账号关注问题的解答
    【实用软件】电脑wifi密码查看器
    低代码+BPM为一体构建的知识管理系统
  • 原文地址:https://blog.csdn.net/weixin_55608297/article/details/128072358