不管是在日常的开发工作中,还是在前端学习中,都经常会听到防抖和节流这样的专业名词,那么防抖和节流到底是何方神圣呢?本篇文章就和大家一起来讨论讨论,揭开他们神秘的面纱。
防抖(debounce)
定义:给定两个条件,一个函数,一个指定的时间段,只有该函数在指定时间段内不再被调用,才会在最后执行该函数。
什么?没听懂?举个栗(例)子🌰
饭店大家应该都去过吧,假设现在已经到厨师下班点了,厨师下班前的任务是收拾干净厨房,收拾厨房就是函数,如果半个小时内都没有来客,那么就执行收拾厨房下班的任务,这半个小时就是指定的时间段,如果此时厨师已经等待了20分钟,在第21分钟时来了客人,厨师就必须得炒菜,并且骂骂咧咧,之后继续等待半个小时,如果中途来新客人,又要清除半个小时并重新开始,直到半个小时内都再没有来客,厨师才会打扫厨房下班。
相信通过栗子,聪明的你们也能猜到防抖是如何实现的
实现原理:没错,就是延时器,setTimeout,如果在函数触发时检测到了延时器,那么就把延时器清除,重新定义一个新的延时器,只有在指定的延时内不触发函数,任务才会在指定的延时后执行。
input输入框是利用防抖进行优化的一个典型,如下是没有进行防抖优化的
可以看到,每一次输入都会调用task函数,如果task执行ajax请求或者是需要大量计算的任务,那么这种代码的性能是很差的,这种时候就可以使用防抖来进行优化,优化如下:
debounce函数就是专门用作防抖的函数,由于需要缓存一个timer,所以这边使用闭包处理,可以看到,防抖处理之后并没有在每次输入时执行task,只在无输入的500ms后执行,处理window的resize监听也是同理。
节流(throttle)
定义:同样两个条件,函数在指定的时间段内只会执行一次
听懂了我也要举个栗(例)子🌰
疫情当下,大家都有过排队做核酸的经历吧,假设医护人员做核酸的速度是固定的,1分钟6个人,那么这1分钟就是指定的时间段,给6个人做核酸就是要执行的任务,为了防止核酸窗口拥堵,工作人员会每隔1分钟放行6个人,那么工作人员所做的事情就叫节流。
实现原理:在延时器的基础上增加开关控制器,在节流函数第一次调用时创建一个延时器,并将控制器关闭,在指定的延时后执行函数并将控制器打开。
比如在窗口resize时进行节流
throttle函数就是节流函数,同样使用闭包来缓存一个控制器,可以看到,窗口在不断resize中,每两秒才会执行一次resize这个函数。
防抖和节流对高频事件的处理是非常有效的
其实写到这里防抖和节流的基本功能就已经实现了,不过深入探究会发现这样写的防抖和节流都是有优化空间的,防抖函数如果一直高频触发的话,目标函数就一直无法执行,节流函数如果在指定时间段内只触发了一次,那么目标函数还是得在延迟指定时间段后执行。
基于这两个问题,我们可以思考是否可以将防抖和节流组合起来使用,并且加上立即执行的功能?OK,直接冻(动)手!!
防抖与节流的组合技
代码如下:
组合技代码接收第三个参数,用来做配置项,这里我在防抖代码的基础上增加时间戳来实现节流的功能,每次防抖阶段结束时,都会将组合技函数还原为初始状态,以便下一次触发时不受上一次状态的影响。
调用结果如下:
完整代码我贴在下方,需要者请自取
function combo(func, wait, options) {
let timer = null;
let timestamp = 0;
return function() {
const now = Date.now();
clearTimeout(timer);
if(!timer) timestamp = now;
if(!timer && options?.immediate) func.call(this, ...arguments);
if(options?.timeout) {
if(options.timeout <= wait) {
throw new Error('超时时间不得小于等待时间');
} else if(now - timestamp > options.timeout) {
func.call(this, ...arguments);
timestamp = now;
}
}
timer = setTimeout(() => {
func.call(this, ...arguments);
timestamp = 0;
timer = null;
}, wait);
};
};
对options配置项值的校验还请自行处理,这里就不再赘述了
前端小渣渣的学习总结,如有错误,欢迎指正!!