前端百题斩【021】——通俗易懂的防抖与节流

本文详解了前端性能优化中的防抖和节流技术,通过实例演示了如何实现防抖和节流函数,以及它们在用户输入校验和滚动事件中的应用,提升页面响应速度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写该系列文章的初衷是“让每位前端工程师掌握高频知识点,为工作助力”。这是前端百题斩的第21斩,希望朋友们关注公众号“执鸢者”,用知识武装自己的头脑。

性能一直是前端老生常谈的一个话题,其中有一个性能问题就是我们会频繁的触发一些事件,例如mousemove、scroll、resize等,虽然浏览器已经对这些事件的触发做了一些优化,但是如果在很短的时间内频繁的触发仍然会影响性能,这个时候就需要今天的主角:防抖和节流,利用它们来进行优化,提高性能。

21.1 防抖

img
21.1.1 定义

防抖就是将多次高频操作优化为只在最后一次执行(某个函数在某段时间内,无论触发了多少次回调,都只执行最后一次)。通常的使用场景是:用户输入,只需在输入完成后做一次输入校验即可。

21.1.2 实现

防抖是将多次操作合并为一次操作完成,其原理就是维护一个计时器,在规定的时间后触发函数,但是在该规定时间内再次触发的话就会取消之前的定时器而重新设置,从而保证了只有最后一次操作能够被触发。其实现步骤如下所示:

  1. 利用闭包保存一个timer变量,然后返回一个函数(这个返回的函数就是后续频繁触发操作中调用的函数);

  2. 根据标志位判断是否第一次需要立即执行(因为有些情况是需要首次调用函数立即执行的,若没有该参数,就会在定时器到了之后才会执行);

  3. 当有新的触发时,若存在定时器,则清空该定时器;

  4. 设定一个新的定时器,重新计时。

function debounce(fn, wait, immediate) {
        let timer = null;
        return function (...args) {
            // 立即执行的功能(timer为空表示首次触发)
            if (immediate && !timer) {
                fn.apply(this, args);
            }

            // 有新的触发,则把定时器清空
            timer && clearTimeout(timer);
            // 重新计时
            timer = setTimeout(() => {
                fn.apply(this, args);
            }, wait);
        }
    }
21.1.3 效果预览

观察效果图可以验证上述的理论知识:

  1. 防抖之后输出内容的频次降低了;

  2. 防抖之后,其在超过一定时间之后才会输出内容。

21.2 节流

img
21.2.1 定义

节流就是每隔一段时间后执行一次,也就是降低频率,将高频操作优化成低频操作。通常使用场景:滚动条事件、resize事件、动画等,通常每隔100-500ms执行一次即可。

21.2.2 实现

节流函数的实现方式有两种:定时器版本、时间戳版本,这两者各有千秋,下面来简要实现一下。

21.2.2.1 定时器版本

定时器版本的节流函数其重点是利用闭包保存timer变量,具有两个特点:

  1. n秒后才会执行第一次(定时器到了时间后才会触发);

  2. 停止触发后节流函数还会执行一次(因为该函数是延迟执行的,当停止触发时其任务已经到了队列中,所以停止后还会执行一次)。

// 定时器版本
function throttle(fn, wait) {
    let timer = null;
    return function(...args) {
        if (!timer) {
            timer = setTimeout(() => {
                fn.apply(this, args);
                timer = null;
            }, wait)
        }
    }
}
21.2.2.2 时间戳版本

时间戳版本的节流函数重点是利用闭包保存上一次的时间previous,具有两个特点:

  1. 开始触发后会立即执行(因为previous开始会被赋值为0);

  2. 停止触发后不再执行(因为该函数是同步任务,在触发的时候就会进行相应的判断,所以就不存在停止触发后再执行的情况)。

// 时间戳版本
function throttle(fn, wait) {
    // 上一次执行时间
    let previous = 0;
    return function(...args) {
        // 当前时间
        let now = +new Date();
        if (now - previous > wait) {
            previous = now;
            fn.apply(this, args);
        }
    }
}
21.2.3 效果预览


观察效果图可以验证上述的理论知识:

  1. 节流确实降低了内容的输出频率,将高频变为低频;

  2. 时间戳版本的节流函数在首次会输出内容,但是最后一次的内容不会输出(谨慎使用);

  3. 定时器版本的节流函数确实不会立刻打印内容,而是超过一定时间之后才会打印;此外,其最后输入的内容会被打印出来。

1.如果觉得这篇文章还不错,来个分享、点赞、在看三连吧,让更多的人也看到~

2.关注公众号执鸢者,领取学习资料,定期为你推送原创深度好文

3.关注公众号进群,里面大佬多多,一起向他们学习

1. 前端百题斩[001]——typeof和instanceof

2. 前端百题斩【002】——js中6种变量声明方式

3. 前端百题斩【003-004】——从基本类型、引用类型到包装对象

4. 前端百题斩【005】—— js中9种遍历对象的方法

5. 前端百题斩【006】——js中三类字符串转数字的方式

6. 前端百题斩【007】——js中必须知道的四种数据类型判断方法

7. 前端百题斩【008-009】——从JavaScript的代码执行过程到函数执行过程

8. 前端百题斩【010】——通俗易懂的JavaScript执行上下文

9. 前端百题斩【011】——通俗易懂的变量对象

10. 前端百题斩【012】——js中作用域及作用域链的真面目

11. 前端百题斩【013】——用“闭包”问题征服面试官

12. 前端百题斩【014】——js中的这些“this”指向都值得了解

13. 前端百题斩【015】——快速手撕call、apply、bind

14. 前端百题斩【016】——原型、构造函数和实例之间的奇妙关系

15. 前端百题斩【017】——一基础、二主线、双机制理解原型链

16. 前端百题斩【018】——从验证点到手撕new操作符

17. 前端百题斩【019】——数组中方法原理早知道

18. 前端百题斩【020】——竟然有五种方式实现flat方法

19. canvas从入门到猪头

20. 前端工程师的一大神器——puppeteer

21. 2021 年前端宝典【超三百篇】

22. 前端也要懂机器学习(上)

23. 前端也要懂机器学习(下)

24. 学架构助力前端起飞

25. 假如只剩下canvas标签

26. Vue源码思想在工作中的应用

27. 一文搞定Diff算法

28. 百度、小红书三面,均遇“赛马”问题

29. 十五张图带你彻底搞懂从URL到页面展示发生的故事

30. 一文搞懂Cookie、Storage、IndexedDB

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值