防抖函数有两种核心变体:延迟执行版(首次不执行,最后一次触发后wait毫秒执行)和立即执行版(首次触发立即执行,后续在冷却期外才可再执行),二者区别在于执行时机判断逻辑与定时器管理策略。

防抖函数(Debounce)的核心是“等用户停下来再执行”,但实际开发中常需要两种变体:一种是首次触发立即执行、后续连续触发则重置计时(即“立即执行版”);另一种是首次触发不执行,等停止触发后延迟执行(即“延迟执行版”,也是最常见默认形式)。两者关键区别在于 执行时机的判断逻辑 和 定时器的清除与启动时机。
延迟执行版(标准防抖)
这是最典型的实现:无论触发多少次,只在最后一次触发后的 wait 毫秒后执行一次回调。
- 每次触发时先清除已有定时器,再新建一个
- 回调函数始终在定时器到期后调用,不会在首次触发时立刻运行
- 适合搜索框输入联想、窗口 resize 后重新布局等场景
示例代码:
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
立即执行版(leading edge debounce)
首次触发时立刻执行回调,之后进入“冷却期”:在 wait 毫秒内再次触发会被忽略;冷却期结束后,下一次触发又会立即执行。
- 靠一个标志位(如
isImmediate)或内部状态区分是否处于冷却期 - 首次触发:若无活跃定时器,则立即执行,并启动一个仅用于标记冷却期的定时器(不执行回调)
- 冷却期内的触发:只清除旧定时器,不执行回调,也不重设新定时器
示例代码:
function debounceLeading(func, wait) {
let timeout = null;
return function(...args) {
const callNow = !timeout;
if (callNow) func.apply(this, args);
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
}, wait);
};
}
支持立即 + 延迟双模式的通用版本
通过增加 immediate 参数,让同一个函数可切换行为。注意:二者互斥,不能同时为 true 执行两次。
- 当
immediate === true:首次触发立即执行,后续触发在冷却期外才可能再次立即执行 - 当
immediate === false(默认):严格延迟执行,类似标准版 - 关键点是把“是否已执行过”和“是否有待执行定时器”两个状态分开管理
示例代码:
function debounce(func, wait, immediate = false) {
let timeout;
return function(...args) {
const later = () => {
timeout = null;
if (!immediate) func.apply(this, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(this, args);
};
}
使用时要注意的细节
-
this 指向:确保回调中
this正确,推荐用func.apply(this, args)或箭头函数配合绑定 -
参数透传:用
...args收集并展开,兼容任意参数个数 -
取消防抖:可扩展添加
cancel方法(清空定时器并重置状态),适用于组件卸载等场景 - 与节流区别:防抖关注“停顿后执行”,节流关注“固定频率执行”,不要混淆使用场景
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











