第一章:混乱的王国
在数字王国里,有个急躁的体操王子👑,他每次练功都疯狂触发动作请求,导致王宫服务器濒临崩溃!国王召见了神秘的时间守护者——防抖侠丹尼,他带着三件神器降临:
1️⃣ 「冷静沙漏」(wait):强制每次动作后必须冷静等待
2️⃣ 「即刻之刃」(leading):可立即响应首个紧急请求
3️⃣ 「最终钟摆」(trailing):确保处理最后一个请求
第二章:恶龙大作战
🐉 恶龙一:时间倒流
巫师篡改系统时钟,导致计时混乱!丹尼掏出沙漏检测到timeSinceLastCall < 0,大喝:"时光逆流?立即执行! " 一剑斩断异常状态。
🦹 恶龙二:无限重置
敌人用高频攻击让沙漏永远重置。丹尼祭出终极武器:「maxWait盾牌」,怒吼:"任你疯狂攻击,每200毫秒必反杀一次! "
📜 恶龙三:假传圣旨
有人冒充国王传假指令(非函数参数)。丹尼慧眼识破,抛出TypeError卷轴:"非函数,斩立决! "
第三章:运作原理
🔑 三神器组合技:
- 「冷静沙漏+最终钟摆」 = 传统防抖
- 「即刻之刃+最终钟摆」 = 首尾兼顾模式
- 「maxWait盾牌」 = 防永久沉默保险
终章:王国的启示
当丹尼卸下战甲,人们发现铠甲由这些代码铸造:
/**
* 防抖函数:控制函数执行频率,支持立即执行和最大等待时间
* @param {Function} func - 需要防抖的目标函数
* @param {number} wait - 等待时间(毫秒)
* @param {Object} [options] - 配置选项
* @param {boolean} [options.leading=false] - 是否立即执行首次调用
* @param {boolean} [options.trailing=true] - 是否执行最后一次调用
* @param {number} [options.maxWait] - 最大等待时间(保证最低执行频率)
*/
function debounce(func, wait, options = {}) {
// 参数校验
if (typeof func !== 'function') {
throw new TypeError('Expected a function');
}
// 配置解析(使用默认值+解构语法)
const {
leading = false,
trailing = true,
maxWait: customMaxWait
} = options;
const hasMaxWait = customMaxWait !== undefined;
const maxWait = hasMaxWait ? Math.max(customMaxWait, wait) : null;
// 状态管理(封装为对象提升可读性)
const state = {
lastThis: null, // 最后一次调用的this上下文
lastArgs: null, // 最后一次调用的参数
lastCallTime: null, // 最后一次触发防抖的时间戳
lastInvokeTime: 0, // 最后一次实际执行函数的时间戳
timeoutId: null // 当前定时器ID
};
// 核心方法:执行目标函数
const invokeFunction = (currentTime) => {
state.lastInvokeTime = currentTime;
state.lastArgs = state.lastThis = null; // 执行后清空上下文
return func.apply(state.lastThis, state.lastArgs);
};
// 判断是否应该执行
const shouldInvoke = (currentTime) => {
const timeSinceLastCall = currentTime - state.lastCallTime;
const timeSinceLastInvoke = currentTime - state.lastInvokeTime;
// 执行条件(满足其一即可):
return (
state.lastCallTime === null || // 首次调用
timeSinceLastCall >= wait || // 超过等待时间
timeSinceLastCall < 0 || // 系统时间异常
(hasMaxWait && timeSinceLastInvoke >= maxWait) // 超过最大等待时间
);
};
// 计算剩余等待时间
const calcRemainingWait = (currentTime) => {
if (hasMaxWait) {
const timeSinceInvoke = currentTime - state.lastInvokeTime;
const timeSinceCall = currentTime - state.lastCallTime;
const remainingFromWait = wait - timeSinceCall;
const remainingFromMax = maxWait - timeSinceInvoke;
return Math.min(remainingFromWait, remainingFromMax);
}
return wait - (currentTime - state.lastCallTime);
};
// 启动定时器流程
const startTimer = (pendingFunc, delay) => {
clearTimeout(state.timeoutId); // 清除旧定时器
state.timeoutId = setTimeout(pendingFunc, delay);
};
// 后缘调用处理(延迟执行)
const trailingEdge = (currentTime) => {
state.timeoutId = null;
if (trailing && state.lastArgs) {
return invokeFunction(currentTime);
}
state.lastArgs = state.lastThis = null; // 无执行则清空参数
};
// 定时器到期时的处理
const timerExpired = () => {
const currentTime = Date.now();
if (shouldInvoke(currentTime)) {
return trailingEdge(currentTime);
}
// 未满足条件则重启定时器
startTimer(timerExpired, calcRemainingWait(currentTime));
};
// 前缘调用处理(立即执行)
const leadingEdge = (currentTime) => {
state.lastInvokeTime = currentTime;
startTimer(timerExpired, wait); // 启动后缘定时器
return leading ? invokeFunction(currentTime) : null;
};
// 主入口函数
const debounced = function (...args) {
const currentTime = Date.now();
state.lastArgs = args; // 保存最新参数
state.lastThis = this; // 保存调用上下文
state.lastCallTime = currentTime;
// 判断是否需要立即执行
if (shouldInvoke(currentTime)) {
if (state.timeoutId === null) { // 无活跃定时器时触发前缘
return leadingEdge(currentTime);
}
// 处理最大等待时间逻辑
if (hasMaxWait) {
startTimer(timerExpired, wait);
return invokeFunction(currentTime);
}
}
// 无活跃定时器时启动新定时器
if (state.timeoutId === null) {
startTimer(timerExpired, wait);
}
};
// 附加方法:取消延迟执行
debounced.cancel = () => {
clearTimeout(state.timeoutId);
state.timeoutId = null;
state.lastCallTime = state.lastInvokeTime = 0;
state.lastArgs = state.lastThis = null;
};
// 附加方法:立即触发执行
debounced.flush = () => {
return state.timeoutId === null ? null : trailingEdge(Date.now());
};
return debounced;
}
🌟 后世传颂:"真正的战士,不在于频繁出招,而在于在正确的时间,给出致命一击!" ——《防抖侠丹尼法典》