防抖(debounce)
防抖函数:一个需要频繁触发的函数,在规定时间内,只让最后一次生效,前面的不生效。
防抖(debounce):不管事件触发频率多高,一定在事件触发n秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,就以新的事件的时间为准,n秒后才执行,总之,触发完事件 n 秒内不再触发事件,n秒后再执行。
1.运用场景一
场景:延迟执行函数
对于连续的事件响应我们只需要执行一次回调:
- 每次 resize/scroll 触发统计事件
- 百度查询,账号文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证最后一次就好)
效果:多次触发都是延迟执行。
实现:
function debounce(event, time) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
event.apply(this, args);
}, time);
};
}
效果为:连续点触发,触发多次事件只执行最后一次。
2.运用场景二
场景:有时候我们需要让函数先立即执行一次,再等后面事件触发后等待n秒执行。以后再多次触发时,只执行最后一次。
我们给debounce函数一个flag用于标示是否立即执行。
效果:连续触发事件,一共执行两次:一开始立即执行一次和最后一次。以后再多次触发时,只执行最后一次。
实现:
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);
};
}
3.运用场景三
立即执行一次,例如按钮防抖,防止用户多次点击(下一次调用必须与前一次调用的时间间隔大于wait才会触发)。
用户给interviewMap点star的时候,我们希望用户点第一下的时候就去调用接口,并且成功之后改变star按钮的样子,用户就可以立马得到反馈是否star成功了,这个情况适用立即执行的防抖函数,它总是在第一次调用,并且下一次调用必须与前一次调用的时间间隔大于wait才会触发。
效果:多次触发事件,立即执行一次。
实现:
function debounce(func, wait, immediate) {
var timeout;
return function (...args) {
if (timeout) {
clearTimeout(timeout);
}
if (immediate) {
// 如果已经执行过,不再执行
var callNow = !timeout;
timeout = setTimeout(function () {
timeout = null;
}, wait);
if (callNow) func.apply(this, args);
} else {
timeout = setTimeout(() => func.apply(this, args), wait);
}
};
}
节流
节流(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);
}
}
}