防抖和节流是解决高频触发问题的两种不同策略:防抖等待最后一次调用再执行,适用于搜索输入、resize;节流确保固定间隔至少执行一次,适用于鼠标移动、滚动监听。

防抖和节流不是“选一个就行”的功能,而是解决不同场景下高频触发问题的两种思路:防抖等最后一次调用,节流保证固定间隔至少执行一次。
防抖函数怎么写?重点在清除定时器
典型场景是搜索框输入、窗口 resize。用户连续输入时,只在停顿后执行一次回调。
- 核心逻辑是每次触发都
clearTimeout上一次的定时器,再新建一个 - 注意闭包保存的
timerId必须在函数作用域内,不能全局污染 - 首次调用是否立即执行?由
immediate参数控制,但多数业务场景(如搜索)不需要立即执行 - 示例:
function debounce(func, wait, immediate = false) { let timerId; return function(...args) { if (timerId) clearTimeout(timerId); if (immediate && !timerId) func(...args); timerId = setTimeout(() => { if (!immediate) func(...args); timerId = null; }, wait); }; }
节流函数怎么写?关键在时间戳或定时器状态判断
适合鼠标移动、滚动监听等需要稳定频率响应的场景。常见实现分时间戳版和定时器版,前者更简洁常用。
- 时间戳版:记录上次执行时间,当前时间减去上次时间 > 等待时间才执行,并更新时间戳
- 定时器版:用
timerId判断是否正在等待中,没在等待就立即执行并设定时器;等待中则不处理 - 注意:时间戳版可能在停止触发后漏掉最后一次,定时器版能保证“收尾”,按需选择
- 示例(时间戳版):
function throttle(func, wait) { let previous = 0; return function(...args) { const now = Date.now(); if (now - previous >= wait) { func(...args); previous = now; } }; }
实际使用时容易忽略的坑有哪些?
写对了函数,不代表用对了场景。很多 bug 出在绑定方式和 this 指向。
立即学习“Java免费学习笔记(深入)”;
- 直接传入
debounce(handleClick, 300)绑定事件时,this会丢失 —— 必须用.bind(this)或箭头函数包裹,或在 debounce 内部用func.apply(this, args) - Vue / React 中不要在 render 或 template 里反复创建防抖函数,会导致每次渲染都新建闭包,破坏缓存和性能 —— 应该在组件初始化时定义一次
- 节流的
wait值不是越小越好。设成 16ms(≈60fps)看似流畅,但若函数本身耗时超过 16ms,反而造成队列堆积,建议从 100ms 起调 - 别在防抖/节流函数里做副作用操作(比如直接修改 ref 或 state),应确保被包装的函数是纯的、可预测的
真正难的不是写出这两个函数,而是在滚动、拖拽、表单联动等复合交互里,判断该用哪个、参数设多少、this 和上下文怎么保全 —— 这些细节往往比函数本身更影响稳定性。











