JavaScript 防抖、节流
debounce 定义
防抖(debounce
):不管事件触发频率多高,一定在事件触发n
秒后才执行,如果你在一个事件触发的 n
秒内又触发了这个事件,就以新的事件的时间为准,n
秒后才执行,总之,触发完事件 n
秒内不再触发事件,n
秒后再执行。(类比乘坐电梯)
应用场景
窗口大小变化,调整样式
window.addEventListener('resize', debounce(handleResize, 200));
搜索框,输入后1000毫秒搜索
debounce(fetchSelectData, 300);
表单验证,输入1000毫秒后验证
debounce(validator, 1000);
图示
实现
注意考虑两个问题:
- 在
debounce
函数中返回一个闭包,这里用的普通function
,里面的setTimeout
则用的箭头函数,这样做的意义是让this
的指向准确,this
的真实指向并非debounce
的调用者,而是返回闭包的调用者。 - 对传入闭包的参数进行透传。
function debounce(event, time) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
event.apply(this, args);
}, time);
};
}
有时候我们需要让函数立即执行一次,再等后面事件触发后等待n
秒执行,我们给debounce
函数一个flag
用于标示是否立即执行。
当定时器变量timer
为空时,说明是第一次执行,我们立即执行它。
function debounce(event, time, flag) {
let timer = null;
return function (...args) {
clearTimeout(timer);
if (flag && !timer) {
event.apply(this, args);
}
timer = setTimeout(() => {
event.apply(this, args);
}, time);
};
}
throttle 定义
节流(throttle
):不管事件触发频率多高,只在单位时间内执行一次。(类比乘坐地铁)
应用场景
降低事件触发频率。比如懒加载时要监听计算滚动条的位置,但不必每次滑动都触发,可以降低计算的频率,而不必去浪费资源。
图示
实现
有两种方式可以实现节流,使用时间戳和定时器。
时间戳实现
第一次事件肯定触发,最后一次不会触发
function throttle(event, time) {
let pre = 0;
return function (...args) {
if (Date.now() - pre > time) {
pre = Date.now();
event.apply(this, args);
}
}
定时器实现
第一次事件不会触发,最后一次一定触发
function throttle(event, time) {
let timer = null;
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
timer = null;
event.apply(this, args);
}, time);
}
}
}
结合版
定时器和时间戳的结合版,也相当于节流和防抖的结合版,第一次和最后一次都会触发
function throttle(event, time) {
let pre = 0;
let timer = null;
return function (...args) {
if (Date.now() - pre > time) {
clearTimeout(timer);
timer = null;
pre = Date.now();
event.apply(this, args);
} else if (!timer) {
timer = setTimeout(() => {
event.apply(this, args);
}, time);
}
}
}