debounce只在事件停止后执行一次,因其每次触发均重置定时器,仅当用户停手且延迟时间耗尽才执行;throttle则通过时间戳或开关控制,确保固定间隔内最多执行一次,首次触发即执行。

debounce 为什么只在事件停止后执行一次
因为它的核心逻辑是「重置计时器」:每次事件触发(比如 input、resize)都会调用 clearTimeout 清掉上一个定时器,再用 setTimeout 启一个新的。只有当用户彻底停手、且等待时间(如 300ms)走完,函数才真正执行。
常见错误现象:debounce 被误用于滚动加载——用户快速滚动时,永远等不到“停止”,导致内容一直不加载。
使用场景要满足两个条件:
- 操作天然具有「连续性」(如输入、拖拽调整)
- 你只关心最终状态,不关心中间过程(比如搜索关键词,只发最后一次)
throttle 怎么保证固定间隔内最多执行一次
它靠时间戳或开关控制:典型实现里会记录上一次执行的 lastTime,每次触发时用 Date.now() - lastTime >= delay 判断是否够间隔;或者用 canRun 布尔值配合 setTimeout 控制窗口期。
关键差异点:
-
throttle的首次触发立刻执行(响应快),而debounce首次触发也要等延迟结束(响应慢) -
throttle不会丢失所有中间事件——只要间隔够,就执行;debounce会丢掉除最后一次外的所有触发 - 滚动监听必须用
throttle,否则用户滚到底部时可能根本收不到“已到临界点”的信号
为什么不能把 debounce 直接套在 scroll 事件上
因为用户滚动行为极少真正“停止”:哪怕只是惯性滑动,也会持续触发 scroll,debounce 的定时器不断被清除,回调永远不会跑。结果就是懒加载失效、吸顶导航卡住、滚动位置监听失灵。
真实问题表现:
- 页面滚动到底部,
loadMore一直不触发 - 滚动过程中
position: sticky元素错位,因依赖scroll计算偏移但没收到更新 - 用
debounce处理mousemove,鼠标划过区域时完全无响应
正确做法:优先选 throttle;若需更平滑,可结合 requestAnimationFrame,而不是加长 debounce 延迟。
实际写法里最容易漏掉的细节
两个函数都必须正确绑定 this 和参数,否则在事件回调中 this 指向 window 或 undefined,arguments 也拿不到原始事件对象。
典型翻车点:
- 直接写
debounce(handler, 300)但没传参,handler里访问不到event.target.value - 用箭头函数包装节流,导致
this绑定失效,Vue/React 组件方法调用时报undefined - 没清理定时器引用,导致组件卸载后定时器还在跑,引发内存泄漏和报错
Cannot read property 'xxx' of null
最稳妥的写法是始终用 func.apply(this, arguments) 或解构参数,且在组件销毁时手动 clearTimeout。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











