防抖和节流是针对不同高频场景的独立策略:防抖等最后一次操作执行,节流保证单位时间至少执行一次;二者核心均依赖闭包维持定时器或时间戳状态,确保多次调用间状态不丢失且不污染全局。

防抖和节流不是“挑一个用”的工具,而是针对不同高频触发场景的两种独立策略:防抖等最后一次操作再执行,节流则保证单位时间内至少执行一次。它们的规范化实现,核心都依赖闭包——让定时器或时间戳这类状态变量在多次函数调用间持续存在、不被释放。
防抖函数:清除旧定时器 + 重设新延迟
适用于搜索输入、窗口 resize 等“只关心最终结果”的场景。关键点是每次触发都先清除上一次 pending 的定时器,再设置新的。
- 用 let timer = null 在外层声明,靠闭包维持引用,避免全局污染
- 返回函数中必须用 func.apply(this, arguments) 正确传递上下文和参数(不能直接传 args 数组,除非显式展开)
- 支持 immediate 参数时,需区分 leading(首次立即执行)和 trailing(停顿后执行),多数搜索场景只需 trailing 模式
- 增强版可挂载 cancel() 方法,用于组件卸载前主动清空 pending 定时器
节流函数:时间戳判断 or 定时器锁控制
适合鼠标移动、滚动监听等需要稳定响应节奏的场景。主流实现分两类:
- 时间戳版:记录上次执行时间,当前时间减去上次时间 ≥ wait 才执行并更新时间戳;简洁但可能漏掉最后一次触发
- 定时器版:用 timer === null 判断是否空闲,空闲则立即执行并设定时器;等待中则跳过;能兜底“收尾”,更稳妥
- 两者都需注意 this 绑定和参数传递,推荐统一用 func.apply(context, arguments)
闭包在这里起什么作用?
闭包不是炫技,而是功能必需。timer 或 previous 这类状态变量必须被内层函数持续访问和修改,又不能暴露到全局——只有闭包能天然满足这点。
- 外层函数执行后,timer 变量不会被回收,因为返回的内层函数仍持有对其的引用
- 每次事件触发调用的都是同一个“包装函数”,它读写的是同一份闭包内的状态
- 没有闭包,每次调用都会新建 timer,防抖/节流逻辑就完全失效
实际使用中的常见坑
写得对,不等于用得稳。几个高频出错点:
-
this 丢失:直接把防抖函数赋给事件监听器(如
input.addEventListener('input', debounce(fn, 300))),fn 内部的 this 会指向 element,而非预期对象;建议提前 bind 或用箭头函数包裹 -
参数传递错误:误把 event 对象当单个参数传入,应使用
arguments或扩展运算符...arguments -
delay 类型校验缺失:用户可能传字符串或 null,建议做
Number(delay) || 300防御处理 - 未考虑组件销毁:React/Vue 中,防抖函数若绑定在 useEffect 或 mounted 里,卸载时记得调用 cancel 清除定时器
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











