防抖(debounce)和节流(throttle)是什么,如何实现它们,它们之间又有什么区别呢?
在前端开发中会遇到一些频繁的事件触发,比如:
window
的resize
、scroll
mousedown
、mousemove
keyup
、keydown
如何解决:防抖、节流
防抖
动作绑定事件,动作发生后一定时间后触发事件,在这段时间内,如果该动作又发生,则重新等待一定时间再触发事件。
具体方法是:在上一次事件触发之后的 time
时间内如果事件没有再次触发,那么就在 time
时间后触发,否则将触发的时间作为新的起始点
通俗理解就是,将密集的时间触发事件看作一个时间整体,整个整体只触发一次事件,密集的界定就是相邻事件触发时间小于 time
,则以这个整体的最后一次事件触发为起始点
可以用数轴来理解,比如有 [2, 5]
和 [8, 11]
两个区间可以看作密集事件发生处,那么整个 [2, 5]
时间最终都只触发一次事件,5 是这次事件结束时,那么等待 time = 3
才再次触发下一次事件
具体代码:
function debounce(func, time) {
let timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, arguments);
}, time);
};
}
这里有一个疑惑,为什么要使用闭包?
原因是 timer
变量是在 debounce
函数内部初始化的时候声明的,但是之后每一次触发返回的箭头函数的时候都需要读取 timer
变量,所以要使用闭包
还有一个疑惑,为什么要使用
func.apply(this, arguments)
而不直接使用func()
?
原因是每次调用箭头函数的时候为了确保上下文环境为当前的 this
,就需要使用 apply
语法
节流
动作绑定事件,动作发生后一段时间后触发事件,在这段时间内,如果动作又发生,则无视该动作,直到事件执行完后,才能重新触发。
节流和防抖大致相同,不同点在于:
- 防抖是“随机应变”,以密集的事件触发处的末尾为下一次事件触发的计时开始处
- 节流是“铁打不动”,始终以第一次事件触发为起始点,忽略
time
时间内的所有事件,第一次事件发生time
事件之后触发第二次事件
在数轴上理解的相同点和区别为:
在 [2, 5]
时间内触发的所有事件都浓缩为一次事件,区别是:防抖将此次事件的发生事件定在最后一次事件触发的时间,节流将时间定在第一次事件触发的时间
节流的代码:
function throtte(func, time) {
let activeTime = 0;
return function () {
const current = Date.now();
if (current - activeTime > time) {
func.apply(this, arguments);
activeTime = Date.now();
}
};
}
从代码的区别也可以看出:
防抖实际上是在事件密集触发区不断更新定时器,而节流只需要不断读取当前时间并且判断是否满足要求即可