防抖支持立即执行(leading edge)与非立即执行(trailing edge)模式自由切换,核心是通过 immediate 布尔参数控制首次行为,并用 isinvoking 状态协调首尾执行冲突,同时保证 this 与参数正确传递,并提供 cancel/flush 方法。

防抖中实现“非立即执行”与“立即执行(Leading Edge)”两种模式的自由切换,关键在于用一个布尔参数控制首次触发行为,并配合内部状态标记避免重复执行。不是简单加个 if 判断,而是要协调定时器、执行时机和状态重置三个环节。
核心逻辑:用 immediate 参数决定首次是否立刻调用
传入一个 immediate 布尔值(如 debounce(fn, 300, true)),true 表示启用 Leading Edge 模式(首次触发立刻执行),false 表示默认的 Trailing Edge 模式(只在停止触发后执行)。
- 当
immediate === true:第一次调用时,若没有正在运行的定时器(即!timeout),就立刻执行函数,并设一个标志(如isInvoking = true)防止后续重复触发 - 当
immediate === false:跳过首次立即执行,只保留定时器逻辑,等延迟结束后再调用 - 无论哪种模式,每次新触发都必须
clearTimeout(timeout),这是防抖成立的前提
避免首次和末次同时执行的冲突
如果 immediate 和 trailing 同时为 true(比如想首尾各执行一次),不能让函数被调两次。需引入一个内部状态变量(如 isInvoking)来协调:
- 首次触发且
immediate为 true → 执行函数,并设isInvoking = true - 后续触发 → 只重置定时器,不执行函数
- 定时器到期时 → 检查
isInvoking是否为 true;若是,说明已执行过首次,本次不再执行;否则才执行(即保证 trailing 仅在未触发过 leading 时生效) - 定时器回调执行完后,重置
isInvoking = false,为下一轮做准备
正确传递 this 和参数,保持上下文完整
无论哪种模式,都要确保原函数的 this 和所有参数被准确捕获并传递:
- 在闭包内保存
const context = this和const args = Array.from(arguments)(或用 rest 参数...args) - 立即执行时用
func.apply(context, args) - 延迟执行时在
setTimeout回调里同样用func.apply(context, args) - 不要直接写
func(...args),否则会丢失原始this绑定
支持手动取消与刷新,增强实用性
自由切换模式的同时,应保留 cancel 和 flush 能力,方便外部干预:
-
cancel():清除定时器、重置isInvoking、清空缓存的pendingArgs和pendingThis -
flush():若有 pending 状态(比如因 leading 已执行但 trailing 还没到时间),则立刻用apply执行一次,再清空 - 这两个方法不依赖
immediate设置,对所有模式通用











