包阅导读总结
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秒)后执行处理函数。
- 重复触发: 如果在定时器到期之前又触发了事件,则清除之前的定时器,并重新设置一个新的定时器。
- 最终执行: 只有当定时器到期而没有新的事件触发时,才会执行处理函数
二:普通防抖
下面根据防抖的核心,来一个最朴素的防抖
-
获取按钮元素:
let btn = document.getElementById('btn');
: 获取 ID 为btn
的按钮元素。
-
定义处理函数
handle
:function handle(e) { ... }
: 这个函数会在用户点击按钮时执行。它接收一个事件对象e
作为参数,但在这个例子中并没有使用这个参数。函数内部目前仅打印 “提交” 到控制台。
-
定义防抖函数
debounce
:-
function debounce(fn) { ... }
: 这个函数接受一个函数fn
作为参数。 -
let timer = null;
: 定义一个timer
变量,用于保存定时器的引用。 -
return function(e) { ... }
: 返回一个新的匿名函数,这个匿名函数将在每次点击时被调用。clearTimeout(timer);
: 清除之前设置的定时器,以防用户在1秒内多次点击按钮。timer = setTimeout(fn, 1000);
: 设置一个新的定时器,在1秒后调用传入的函数fn
。
-
-
添加点击事件监听器:
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 执行机制和闭包。下面我来解释下这里的闭包作用:
-
闭包的定义:
-
变量
timer
的作用域:
-
闭包的作用:
- 闭包使得
timer
变量可以在多次调用返回的匿名函数之间保持状态。每次点击按钮时,返回的匿名函数都可以访问到同一个timer
变量,从而实现防抖的效果。
三:优化普通防抖
不知道友友们有没有发现一个问题,在上述防抖代码中,我们在handle
函数中输出this
,会发现这里的this
指向了全局,这不对啊,这个this
应该是指向btn
才对,做了一个防抖节流,把人家this
指向给掰弯了(如果友友们对this的指向不太理解,可以参考这篇文章:this this,你到底指向谁)
原因很简单,当定时器到期时,fn
(即handle
)会被调用。在定时器的回调函数中,如果没有明确绑定this
的值,那么在非严格模式下,this
会默认指向全局对象(通常是window
),那么我们现在要怎么做才能正确的将handle
中的this
指回btn
呢?
-
找出能够指向
btn
的函数:匿名函数(return function(e) { ... }
)是在debounce
函数内部定义的,它是在按钮点击事件触发时执行的,由于它是作为事件监听器的一部分被执行的,因此它的this
值会自动指向触发事件的元素,即btn
。 -
利用显示绑定强行掰弯
handle
里的this
,使其指向btn
:此外,我们再加上事件参数,以下是两份完整的优化后代码:
使用call
或apply
方法:
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));
四:高级防抖
聊了这么久基础,终于进入难点了,简单的防抖还不足以打动面试官,我们要学习更高级,性能更好的防抖。
手写高级防抖
-
定义防抖函数
debounce
: -
内部变量:
-
返回新的函数:
-
使用防抖函数:
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);
与普通防抖的不同
-
立即执行模式:
- 新版本的防抖函数支持立即执行模式,即如果
immediate
为true
,则在首次触发事件时立即执行func
,并在wait
时间后清除定时器。这在某些场景下非常有用,例如当需要立即响应用户的第一次动作,但后续动作需要防抖处理时。
- 新版本的防抖函数支持立即执行模式,即如果
-
更灵活的参数:
- 新版本的防抖函数接受第三个参数
immediate
,使得开发者可以根据具体需求选择是否立即执行处理函数。
- 新版本的防抖函数接受第三个参数
-
更完整的实现:
- 新版本的防抖函数不仅返回处理函数的结果,而且在返回前会清除定时器,确保内存资源得到释放。
为什么更优秀
-
灵活性:
- 支持立即执行和延迟执行两种模式,提供了更多的选择性。
-
资源管理:
- 在立即执行模式下,通过设置定时器来清除
timeout
变量,确保了内存资源的有效管理。
- 在立即执行模式下,通过设置定时器来清除
-
用户体验:
- 立即执行模式可以提供更好的用户体验,因为它可以在第一次触发事件时立即响应,之后再进行防抖处理。