防抖需用闭包保存timerId并每次先clearTimeout,节流用setTimeout或requestAnimationFrame实现固定频率执行;搜索框用防抖,滚动监听多用节流,触底检测用防抖。
防抖的核心是「等用户停手再执行」,常见于搜索框输入、窗口缩放监听。关键陷阱是 clearTimeout 没清对上一次的定时器,导致旧回调仍会触发。
必须用闭包保存上一个 timerId,且每次新触发时先清除它:
function debounce(fn, delay) {
let timerId = null;
return function(...args) {
clearTimeout(timerId);
timerId = setTimeout(() => fn.apply(this, args), delay);
};
}timerId(否则多个 debounce 实例互相干扰)fn.apply(this, args) 保证 this 和参数正确传递
hrottle)用 setTimeout 还是 requestAnimationFrame
节流是「固定频率执行」,适合鼠标移动、滚动监听。两种主流实现方式差异明显:
setTimeout 版本:简单可靠,兼容性好,但可能略滞后于真实事件节奏requestAnimationFrame 版本:更贴合屏幕刷新率(通常 60fps),视觉更顺滑,但不适用于需要精确时间间隔的场景(比如每 200ms 发一次请求)基础 setTimeout 节流示例:
立即学习“Java免费学习笔记(深入)”;
function throttle(fn, delay) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= delay) {
fn.apply(this, args);
lastTime = now;
}
};
}注意:lastTime 初始值不能设为 0 以外的固定值,否则首次调用可能被跳过。
搜索框输入后发起请求,99% 的情况该用防抖——因为用户真正关心的是「最终输入结果」,不是中间过程。节流在这里反而容易漏掉最后一次输入。
300~500ms 比较合理;太短用户还没打完就发了,太长响应迟钝AbortController)滚动事件(scroll)非常频繁,不加控制直接绑定回调会导致严重卡顿。这里节流更常用,但防抖也有其不可替代的用途:
IntersectionObserver 替代手动监听 scroll,它天然异步且性能更好,防抖/节流都可省掉别忘了加 passive: true 到 addEventListener 选项里,否则移动端滚动会强制同步执行 JS,直接卡死。