防抖与节流是应对事件节奏的两种思维方式:防抖适用于需等待用户静默(如搜索输入、resize),节流适用于需控制执行频率(如滚动加载、绘线),关键在根据触发场景选择策略并正确维护状态变量与清理逻辑。

防抖与节流不是两个要背的函数,而是两种应对事件节奏的思维方式。学透的关键,在于能根据用户行为反推该用哪个、为什么、怎么调才不翻车。
先盯住触发场景,再选策略
别从代码开始,从用户手指和鼠标动起来的那一刻想:
- 搜索框输入:用户敲“react”,中间停顿0.2秒又敲“js”——你希望发1次请求(含完整关键词),还是在敲“r”“re”“rea”时就发3次?选防抖,等静默。
- 滚动加载更多:用户猛滑到底部,1秒内触发80次scroll——你只要检测“是否到底”这个状态,不需要每像素都算;但也不能等他停下才查,否则白屏卡住。选节流,控节奏。
- 窗口resize重排版:拖拽边框时高频触发,但真正需要渲染的是松手后的最终尺寸。防抖更合适。
- 画板鼠标移动绘线:坐标必须连续采样,但全量记录太卡。节流保节奏,比如每50ms取一个点。
手写时只抓两个变量,别堆逻辑
两者都靠闭包维持状态,但维护的变量不同,直接决定行为:
- 防抖只管 timer:有 timer 就 clearTimeout,再 set 新的;执行完 timer = null。它没有“上次啥时候执行”的概念,只有“当前有没有待执行任务”。
- 节流盯 lastTime 或 valid:时间戳版存上次执行时间,用 Date.now() - lastTime >= delay 判断;定时器版用布尔标记 valid,true 才放行,执行后设 false,setTimeout 里再设回 true。
绕不开的三个实操细节
这些地方一错,功能就失效或反直觉:
- this 和参数必须透传:包装函数里写 func.apply(this, arguments),不能只写 func(),否则上下文丢失、input值拿不到。
- 立即执行不是默认选项:防抖加 immediate: true 才首调即执行;节流的时间戳版天然首调执行,定时器版默认延迟执行——别混着用。
- 清理动作不能省:组件卸载、表单销毁时,记得调用 debouncedFn.cancel() 或手动 clearTimeout;节流若用定时器,执行完必须重置开关,否则锁死。
调试时用时间轴推演,比跑代码更准
拿真实节奏模拟一遍,就能判断逻辑对不对:
- 用户每100ms敲一个字母,共敲5次(h-e-l-l-o),防抖 delay=300ms → 实际只执行1次,在敲完“o”后300ms触发。
- 同样节奏,节流 delay=300ms → 最多执行2次:第1次在敲“h”时(时间戳版立即执行),第2次在敲第2个“l”后300ms(即t=400ms左右),中间3次被丢弃。
- 如果节流用定时器版,首次触发会延迟300ms才执行,不是立刻响应——这点常被忽略。


















