理解闭包理解防抖
本文最后更新于:2024年10月26日 晚上
防抖
防抖主要应用在一些高频次操作事件上,比如:scroll 滑动、input 输入、resize 缩放等。
什么是防抖
防抖的意思是在高频率触发的事件中,只执行最后一次操作,前面的操作都会被取消掉。例如,你在页面中连续滚动,防抖函数会等待一定的时间,如果这段时间内没有再次触发滑动,才执行实际的处理逻辑。如果在等待的时间内事件再次触发,计时器就会被重置,直到没有再触发时,才执行一次操作。
一般来说防抖函数形式如下
使用示例
假设我们有一个函数 handleScroll
,它在页面滚动时处理一些操作,我们不希望它每次滚动都触发,因为这会增加不必要的性能消耗,我们希望它在滚动停止后执行,那么可以这样使用防抖函数
理解防抖函数
为理解防抖函数,首先要看看防抖函数怎么被使用。由上面的使用示例可以知道,调用时,debounce
的第一个参数为我们希望进行管理的高频次响应或处理事件函数,第二个参数为我们希望的连续事件结束后等待的时间。
一进入防抖函数,首先检查是否存在过定时器,如果有则清除,如果没有则新建一个定时器,并在定时器内调用回调,执行真正你想执行的操作。
核心是:多次触发事件时,只有最后一次触发后的延迟时间结束后才执行目标函数。
- 什么是 timer?
在上面的页面滑动例子中,timer
在每次被调用的时候被赋值为定时器的 ID
。
具体来说,setTimeout 的返回值是一个定时器的标识符 ID
。而定时器的 ID 的类型因环境不同也有所差异:在浏览器环境中,setTimeout 的返回是一个数值类型的 ID,例如第一次调用可能返回 1,第二次为 2,依次递增;在 Node.js 环境中,setTimeout 返回值可能是一个定时器对象,但仍然可以通过 clearTimeout 消除。当然也可以将 timer 写作别的名称,只要合法都行,但为了直观还是建议写成 timer
- 为什么每次开始要判断是否清除定时器 ID?
因为定时器 ID 的存在状态标识了某事件是否还在发生。
假设每次开始不清除定时器,那么每一次滑动页面都会设置一个新的定时器,这样并不能做到防抖效果,因为你只是将每一次的滑动进行了延时,最终每一次的滑动处理事件都将发生。
现在,假设每次开始都清除定时器,那么当前的滑动事件会将上一次的滑动事件定时清除,并为自己这一次的滑动事件进行定时,而这一次若为最后一次,函数将不再会被调用,也就不再会清除定时器,这一次的事件将会被正确的以设置好的时间定时进行处理。
这种事件可以类比生活中的乘电梯事件:将电梯门看作事件处理函数,进入电梯的这一动作看作不希望的高频次事件。当人按下电梯按钮,电梯抵达并开门,开门的同时开始计时,每有一个人被红外检测到,人经过电梯门进入电梯之后,电梯门的计时就重新开始(清除上一次的计时),直到没有人进入(高频次事件停止,进入电梯的最后一个人为最后的事件),电梯门最终关闭(定时器时间到了),电梯执行上升或下降等约定好的操作(执行回调函数)。
- 为什么不将 timer 写在外部?为什么返回一个闭包函数?
timer
在功能上看,当然可以作为全局变量定义在整个函数外部,但是这样一来如果别处也要使用 timer
就会显得不太合适,timer
会被污染,不便于函数的封装复用并且还存在安全问题,将其私有化是一个好的选择。
而且闭包可以记录外部变量的状态(这主要依靠作用域链),这样能保证多次触发事件访问到的是同一个timer
,保证多次事件触发能清除到前一个事件的定时,最终实现防抖。
what the hell is this closure anyway?这闭包到底是啥?
闭包(closure)是 JavaScript 中一个重要的概念,闭包是指一个函数可以访问它外部函数作用域中的变量,即使在外部函数已经执行结束后,它仍然可以通过引用这些变量。
什么意思呢?假设有这样一个函数
按理说一个函数 return 之后函数本身就已经 over 了,但是 counter 还是能够访问到 count 的值,并进行了累加。类似这样的一个返回的函数就是闭包。闭包一般是在函数嵌套的情况下形成的。当一个函数 A
返回另一个内部函数 B
,并且 B
可以访问 A
函数内部定义的变量时,闭包就形成了。
...args
是什么?为什么?
在 JavaScript 中,...args
是一种称为 剩余参数(Rest Parameters) 的语法,它用于将函数的不定数量的参数收集到一个数组中。这样可以让函数接受任意数量的参数,并将它们组合在一起,便于操作和处理。
说到为什么要使用它,还得说说它的好处。假设有这样一个函数
不论传入多少参数,有还是没有,它都能够接收并存储在 args
数组中。
那为什么防抖中要用到呢?
一来是为了使得函数能够处理任意数量的参数,使其更通用;二来是为了能够正确地传递参数。可别不以为意,有时候还真没多少人能正确地传递参数。