防抖与节流是什么?如何实现?
2026-03-03 13:13:50
0浏览
收藏
防抖与节流是前端应对高频事件(如输入、滚动、缩放)导致函数过度调用的关键优化手段:防抖适用于“等用户彻底停下再执行”的场景(如搜索建议),确保只响应最终操作;节流则用于“需在持续交互中定期响应但不卡顿”的场景(如滚动监听、鼠标绘图),保障及时性与性能的平衡;二者虽原理不同,但核心挑战都在于正确处理this绑定、参数传递、定时器清理及边界情况,实际开发中强烈推荐使用Lodash或成熟的React/Vue封装方案,而非手写——因为真正决定选型的不是代码多精巧,而是业务语义:用户需要“等停”还是“即时反馈”?

防抖和节流不是 JavaScript 语言层面的必需机制,而是应对高频事件(比如 resize、scroll、input)导致函数被过度调用的实际需求。不加控制时,一个快速拖拽滚动条的动作可能触发上百次 scroll 回调,直接卡死 UI 或浪费计算资源。
防抖适用于“等用户彻底停下来再执行”的场景
典型例子是搜索框的实时建议:用户每敲一个字就发请求,既浪费带宽又让后端压力陡增;改成防抖后,只在用户停顿(比如 300ms)后再发起最后一次查询。
debounce的核心是每次触发都清除前一次定时器,重置倒计时- 必须区分「立即执行」和「非立即执行」:传入
immediate = true可让第一次触发立刻运行,后续触发则重新计时 - 注意 this 和参数绑定:直接传入
func而不处理上下文,回调里this会丢失,推荐用func.apply(context, args)
function debounce(func, wait, immediate = false) {
let timeout;
return function(...args) {
const later = () => {
timeout = null;
if (!immediate) func.apply(this, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(this, args);
};
}
节流适用于“固定频率执行,但不能丢弃所有中间状态”的场景
比如 canvas 绘图跟随鼠标移动,或监听页面滚动位置做吸顶判断。这类逻辑不需要每帧都响应,但也不能等到用户停下才动——否则体验会明显滞后。
throttle常见实现有「定时器版」和「时间戳版」:前者更稳定,后者更省资源但首次触发可能延迟- 定时器版会在上一次执行结束后的 wait 时间内拒绝新调用;时间戳版则靠记录上次执行时间,差值不足 wait 就跳过
- 节流函数内部需保存
context和args,否则多次触发时参数会错乱
function throttle(func, wait) {
let timeout = null;
return function(...args) {
if (!timeout) {
timeout = setTimeout(() => {
func.apply(this, args);
timeout = null;
}, wait);
}
};
}
实际项目中别手写,优先用 Lodash 或封装好的 Hook
手写容易漏掉边界情况:比如未清理定时器导致内存泄漏、未处理 this 绑定、未支持取消(cancel 方法)、未兼容服务端渲染(SSR 中无 setTimeout)。
- Lodash 的
_.debounce和_.throttle支持leading/trailing、maxWait等配置,且自带cancel和flush - React 用户建议用
useDebounce或useThrottle自定义 Hook,避免在组件重渲染时重复创建防抖函数 - 注意:Vue 3 的
watch本身不防抖,需配合lodash/debounce或async-validator类库手动包裹
真正容易被忽略的是「该用哪个」——不是看技术实现多优雅,而是看业务语义:用户输入后要不要等停顿?滚动过程中要不要及时反馈?这两个问题的答案,直接决定该选防抖还是节流,而不是反过来根据函数怎么写来凑场景。
到这里,我们也就讲完了《防抖与节流是什么?如何实现?》的内容了。个人认为,基础知识的学习和巩固,是为了更好的将其运用到项目中,欢迎关注golang学习网公众号,带你了解更多关于的知识点!
BAT文件无法运行解决方法大全
- 上一篇
- BAT文件无法运行解决方法大全
- 下一篇
- 天眼查开票步骤详解及会员流程
