Posted in

还在写普通防抖? – 掘金_AI阅读总结 — 包阅AI

包阅导读总结

1. 防抖、前端性能、优化策略、闭包、定时器

2. 本文主要介绍了防抖的概念、工作原理、普通防抖的实现及优化,还探讨了高级防抖。强调了防抖在前端性能优化中的重要性,以及如何通过不同方式实现更优的防抖效果。

3.

– 防抖意义及其工作原理

– 定义:避免短时间内连续触发函数,减轻系统负担

– 原理:连续触发事件时,一段时间后执行一次处理函数,期间有新事件触发则重新计时

– 流程:初始化、设置定时器、重复触发、最终执行

– 普通防抖

– 代码实现:获取按钮元素,定义处理函数和防抖函数,添加点击事件监听器

– 闭包作用:保持变量`timer`状态,实现防抖效果

– 优化普通防抖

– 问题:`handle`函数中`this`指向全局

– 解决:使用`call`或`apply`方法,或箭头函数,使`this`指回`btn`

– 高级防抖

– 函数定义及参数

– 与普通防抖的不同:支持立即执行模式、更灵活参数、更完整实现

– 优势:灵活性、资源管理、用户体验

思维导图:

文章地址:https://juejin.cn/post/7398462127441428489

文章来源:juejin.cn

作者:midsummer18

发布时间:2024/8/3 10:44

语言:中文

总字数:2671字

预计阅读时间:11分钟

评分:84分

标签:前端开发,性能优化,防抖函数,JavaScript,闭包


以下为原文内容

本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com

不会防抖?没有关系,我们先从普通防抖开始

一:防抖意义及其工作原理

防抖是一种函数调用的优化策略,主要用于避免在短时间内连续多次触发某个函数,从而减轻系统负担。在面试中,也是常考的前端性能优化策略,防抖的主要思想是在一系列连续的事件触发时,只在一段时间之后执行一次处理函数。如果在这段时间内又有新的事件触发,则会重新计时。

  1. 初始化: 用户触发事件(如按钮点击、输入框输入等)。
  2. 设置定时器: 第一次触发事件时,设置一个定时器,计划在一定时间(比如1秒)后执行处理函数。
  3. 重复触发: 如果在定时器到期之前又触发了事件,则清除之前的定时器,并重新设置一个新的定时器。
  4. 最终执行: 只有当定时器到期而没有新的事件触发时,才会执行处理函数

二:普通防抖

下面根据防抖的核心,来一个最朴素的防抖

  1. 获取按钮元素:

    • let btn = document.getElementById('btn');: 获取 ID 为btn的按钮元素。
  2. 定义处理函数 handle:

    • function handle(e) { ... }: 这个函数会在用户点击按钮时执行。它接收一个事件对象e作为参数,但在这个例子中并没有使用这个参数。函数内部目前仅打印 “提交” 到控制台。
  3. 定义防抖函数 debounce:

    • function debounce(fn) { ... }: 这个函数接受一个函数fn作为参数。

    • let timer = null;: 定义一个timer变量,用于保存定时器的引用。

    • return function(e) { ... }: 返回一个新的匿名函数,这个匿名函数将在每次点击时被调用。

      • clearTimeout(timer);: 清除之前设置的定时器,以防用户在1秒内多次点击按钮。
      • timer = setTimeout(fn, 1000);: 设置一个新的定时器,在1秒后调用传入的函数fn
  4. 添加点击事件监听器:

    • btn.addEventListener('click', debounce(handle));: 为按钮添加一个点击事件监听器,监听器的回调函数是debounce(handle)的结果。
    <script>    let btn = document.getElementById('btn');    function handle() {                console.log('提交');     }        function debounce(fn) {        let timer = null;        return function() {                        clearTimeout(timer);            timer = setTimeout(fn, 1000);        };    }        btn.addEventListener('click', debounce(handle));</script>

    根据以上代码,我们能够达到一个防抖的基本要求,其中闭包的利用,是实现这个功能的核心,如果友友们对闭包不太理解,可以参考这篇文章:深入理解 JavaScript 执行机制和闭包。下面我来解释下这里的闭包作用:

    1. 闭包的定义:

    1. 变量 timer 的作用域:

    1. 闭包的作用:

    • 闭包使得timer变量可以在多次调用返回的匿名函数之间保持状态。每次点击按钮时,返回的匿名函数都可以访问到同一个timer变量,从而实现防抖的效果。

三:优化普通防抖

不知道友友们有没有发现一个问题,在上述防抖代码中,我们在handle函数中输出this,会发现这里的this指向了全局,这不对啊,这个this应该是指向btn才对,做了一个防抖节流,把人家this指向给掰弯了(如果友友们对this的指向不太理解,可以参考这篇文章:this this,你到底指向谁)

原因很简单,当定时器到期时,fn(即handle)会被调用。在定时器的回调函数中,如果没有明确绑定this的值,那么在非严格模式下,this会默认指向全局对象(通常是window),那么我们现在要怎么做才能正确的将handle中的this指回btn呢?

  1. 找出能够指向btn的函数:匿名函数(return function(e) { ... })是在debounce函数内部定义的,它是在按钮点击事件触发时执行的,由于它是作为事件监听器的一部分被执行的,因此它的this值会自动指向触发事件的元素,即btn

  2. 利用显示绑定强行掰弯handle里的this,使其指向btn

    此外,我们再加上事件参数,以下是两份完整的优化后代码:

使用callapply方法

let btn = document.getElementById('btn');function handle(e) {        console.log('提交', this); }function debounce(fn) {    let timer = null;    return function(e) {    const that = this                clearTimeout(timer);        timer = setTimeout(function() {            fn.call(that, e);         }, 1000);    };}btn.addEventListener('click', debounce(handle));

使用箭头函数

let btn = document.getElementById('btn');function handle(e) {        console.log('提交', this); }function debounce(fn) {    let timer = null;    return function(e) {                clearTimeout(timer);        timer = setTimeout(() => {             fn.call(this, e);         }, 1000);    };}btn.addEventListener('click', debounce(handle));

四:高级防抖

聊了这么久基础,终于进入难点了,简单的防抖还不足以打动面试官,我们要学习更高级,性能更好的防抖。

手写高级防抖

  1. 定义防抖函数 debounce:

  2. 内部变量:

  3. 返回新的函数:

  4. 使用防抖函数:

    • debounce(getUserAction, 1000, true);: 调用防抖函数,传入getUserAction函数作为处理函数,设置等待时间为 1000 毫秒,并立即执行处理函数
function debounce(func, wait, immediate) {    var timeout, result;         return function() {         var context = this;        var args = arguments;        if (timeout) clearTimeout(timeout);         if (immediate) {            var callNow = !timeout;            timeout = setTimeout(function() {                timeout = null;             }, wait);            if (callNow) result = func.apply(context, args);        } else {            timeout = setTimeout(function() {                result = func.apply(context, args);            }, wait);        }        return result;    };}debounce(getUserAction, 1000, true);

与普通防抖的不同

  • 立即执行模式:

    • 新版本的防抖函数支持立即执行模式,即如果immediatetrue,则在首次触发事件时立即执行func,并在wait时间后清除定时器。这在某些场景下非常有用,例如当需要立即响应用户的第一次动作,但后续动作需要防抖处理时。
  • 更灵活的参数:

    • 新版本的防抖函数接受第三个参数immediate,使得开发者可以根据具体需求选择是否立即执行处理函数。
  • 更完整的实现:

    • 新版本的防抖函数不仅返回处理函数的结果,而且在返回前会清除定时器,确保内存资源得到释放。

为什么更优秀

  • 灵活性:

    • 支持立即执行和延迟执行两种模式,提供了更多的选择性。
  • 资源管理:

    • 在立即执行模式下,通过设置定时器来清除timeout变量,确保了内存资源的有效管理。
  • 用户体验:

    • 立即执行模式可以提供更好的用户体验,因为它可以在第一次触发事件时立即响应,之后再进行防抖处理。