防抖与节流
防抖与节流都是高阶函数。
防抖是让函数延迟执行,多次点击触发事件以最后一次点击为准。
节流是以第一次点击为准,短时间内的多次点击都会被忽略。
节流函数是以防抖函数为基础调用的,所以先实现防抖函数。
防抖
// 防抖函数
function debounce(fn, wait, immediate) {
// immediate参数是在节流函数中调用传递的,防抖函数中不用
let timeout;
// lastTimeInterval 记录两次点击之间的时间间隔,minWait 记录剩余的等待时间,两个都只在节流函数中使用
let lastTimeInterval = 0;
let minWait = wait;
return function (...args) {
const context = this;
if (timeout) clearTimeout(timeout);
if (immediate) {
// 节流
// 根据 timeout 判断下面的重置定时器是否正常运行没有被取消
const callNow = !timeout;
// 记录两次点击的时间间隔
lastTimeInterval = Date.now() - lastTimeInterval;
// 以 wait 作为最大等待时间,每次点击时间间隔很小就会减去这个时间间隔返回剩余等待时间
minWait = minWait < lastTimeInterval ? wait : minWait - lastTimeInterval;
// 定时器执行重置操作,如果短时间内多次点击,minWait 作为剩余等待时间会越来越小
timeout = setTimeout(() => {
timeout = null;
lastTimeInterval = 0;
minWait = wait;
}, minWait);
// 如果 callNow 为 true,说明是第一次调用,或者上次的定时器重置操作正常执行了
if (callNow) fn.apply(context, args);
} else {
// 防抖
timeout = setTimeout(() => {
fn.apply(context, args);
}, wait);
}
};
}
export default debounce;
看上面的注释可以看到,防抖函数很简单,最后的 else 分支就是防抖函数的核心,设置一个定时器来延迟执行传递的函数。
节流
import debounce from "./debounce.js";
// 节流函数
function throttle(fn, wait) {
// 节流就是调用防抖函数,并传递第三个参数为 true
return debounce(fn, wait, true);
}
export default throttle;
因为节流函数就是调用防抖函数,所以去看防抖的 if (immediate) 分支。
节流函数需要两个辅助变量 lastTimeInterval 和 minWait,前者是记录两次调用的时间间隔,后者是记录剩余等待时间,这样可以实现在 wait 时间内,第一次点击会被立即调用,剩下的点击会重新计算时间间隔及剩余等待时间来重新定义重置计时器,而不会去调用传递的函数。
只有重置定时器顺利执行完毕,下次的点击才会像第一次点击一样再次生效。