JavaScript 如何做防抖
你有没有遇到过这种情况:输入框里打几个字,就触发好几次搜索请求;滚动页面时,监听滚动事件的函数疯狂跑,直接把浏览器卡得掉帧?其实这些问题,大多是“函数触发太频繁”导致的,而JavaScript里的防抖,就是专门解决这个麻烦的小工具。
什么是防抖?用生活化例子秒懂
咱们先想个场景:你家小区门口的快递柜,快递员放快递时,不会你刚到门口就立刻开柜门,而是等你站定了、准备掏手机扫码了,柜门才弹开。这就是防抖的核心逻辑——把“多次触发的动作”合并成“最后一次触发后延迟执行”。

对应到代码里,就是:如果用户在1秒内连续触发同一个函数,就不算数,重新计时;直到用户停止触发1秒后,才真正执行函数。比如搜索框的联想功能,用户每打一个字就触发输入事件,要是直接发请求,会给服务器添乱;用了防抖后,用户打完字停个0.5秒,才发请求,既不耽误体验,又能省资源。
手写防抖函数:拆解步骤,一看就会
咱们用最基础的代码例子,把防抖的原理拆成3步,保证你能看懂:
- 先存定时器ID:每次触发函数,先把之前没执行的定时器清掉,相当于“取消上一次的临时动作”;
- 重新计时:用
setTimeout设置新的定时器,约定“多少毫秒后执行真正的函数”; - 暴露可调用的函数:把防抖后的函数返回出去,用的时候直接调它就行。
举个实际的代码片段(不用怕看不懂,都是简单逻辑):
`javascript
// 自定义防抖函数,接收要执行的函数和延迟时间
function debounce(func, delay = 500) {
let timerId; // 存定时器的ID
// 返回新函数,每次调用这个新函数就是触发防抖
return function(...args) {
// 清掉之前的定时器,相当于重置计时
clearTimeout(timerId);
// 设新定时器,delay毫秒后执行原函数,把参数传进去
timerId = setTimeout(() => func.apply(this, args), delay);
};
}
`
你看,这么一小段代码就实现了防抖。要是用它做搜索联想,只要把原来的请求函数传进去就行:
`javascript
// 原来的搜索请求函数
function search(keyword) {
console.log(搜索关键词:${keyword},发请求);
}
// 用防抖包装一下,延迟500毫秒执行
const debouncedSearch = debounce(search, 500);
// 输入框的输入事件就绑这个包装后的函数
inputElement.addEventListener('input', (e) => debouncedSearch(e.target.value));
`
现在用户打“JavaScript”,每打一个字就触发一次debouncedSearch,但每次都会清掉之前的定时器,直到用户打完最后一个字停0.5秒,才会输出“搜索关键词:JavaScript,发请求”——完美解决频繁触发的问题。
用防抖的小细节:别踩这2个坑
知道了原理,用的时候还要注意两个实用小技巧:
第一个是立即执行的防抖:有时候你需要“第一次触发就立刻执行,之后连续触发就防抖”,比如页面滚动到底部加载更多,要是等停止滚动再加载,用户会等很久。这时候只要给防抖函数加个参数immediate就行:
`javascript
function debounceImmediate(func, delay, immediate = false) {
let timerId;
return function(...args) {
if (timerId) clearTimeout(timerId);
// 要是需要立即执行,且当前没定时器,就立刻跑函数
if (immediate && !timerId) func.apply(this, args);
// 不管有没有立即执行,都重置定时器
timerId = setTimeout(() => timerId = null, delay);
};
}
`
第二个是取消定时器:要是用户中途离开页面,或者不需要再触发函数了,可以用clearTimeout清除定时器,避免没必要的执行。比如搜索框失去焦点时,就可以取消未执行的防抖。
其实防抖不是什么复杂的黑科技,就是把“频繁触发的动作”按最后一次的时间点统一处理。平时做前端开发时,遇到频繁触发的场景(输入、滚动、窗口 resize),用个防抖就能轻松解决性能问题,让你的代码又省资源又好用。