防抖函数需用...args传参并fn.apply(this, args)保this,addEventListener要加{passive: false}确保preventDefault有效,混用节流防抖时勿误配immediate参数,组件中须在卸载时清除定时器避免内存泄漏。
直接用 setTimeout + clearTimeout 实现防抖时,最常出问题的是「多次调用后只执行最后一次,但实际执行的却是第一次绑定的参数」。这是因为闭包捕获了外层变量,而没做参数隔离。
...args 收集参数,再通过 fn.apply(this, args) 调用,保留 this 上下文fn(),否则 this 会丢失(尤其在事件监听中)function debounce(fn, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
滚动、触摸等高频事件默认启用了 passive: true,意味着浏览器假设你不会调用 preventDefault(),从而提前优化滚动性能。但如果你在防抖函数里需要阻止默认行为(比如自定义下拉
加载),就会被静默忽略。
{ passive: false } 才能确保 event.preventDefault() 有效element.addEventListener('touchstart', debounce(handleTouch, 100), { passive: false });
防抖(debounce)和节流(throttle)解决的问题不同:前者是「等停了再执行」,后者是「固定频率执行」。强行混用或误配参数会导致逻辑错乱,比如搜索框输入后迟迟不发请求,或窗口缩放时卡顿。
immediate 参数只在节流中有意义,防抖不支持“立即执行+后续忽略”模式delay 值太小(如 10ms)在低端设备上可能失去意义,建议不低于 30ms
setTimeout 版本更稳在组件内创建防抖函数,必须考虑组件卸载后定时器还在运行的风险——这会触发 React 的 “can't perform a React state update on an unmounted component” 或 Vue 的响应式警告。
useRef 存储 timer ID,并在 useEffect 清理函数中 clearTimeout
onBeforeUnmount 钩子里清除定时器;Vue 2 用 beforeDestroy
render 或 setup 函数体顶层,否则每次渲染都新建函数,防抖失效关键点不是“怎么写防抖”,而是“谁负责清理”。漏掉清理,性能优化就变成内存泄漏源头。