js防抖节流函数封装教程
别再用老一套了:一文吃透JS防抖与节流的精准封装
做前端开发,谁没遇到过这种糟心事?窗口拖动时页面卡成PPT,搜索框每敲一个字就发一次请求把服务器干懵了,或者疯狂点击提交按钮导致表单重复上报。这些场景的救星就是防抖和节流。但网上流传的那些“复制粘贴版”代码,往往藏着坑:一旦涉及类方法调用丢失 this,或者异步请求还没返回就再次触发,逻辑直接跑偏。与其到处拼凑碎片代码,不如自己掌握一套能扛住实际业务的封装方案。
防抖:等人齐再关门
防抖的逻辑像极了等电梯。只要里面还有人不断进出,计时器就会重置,直到彻底没人动,电梯门才开。写代码时,核心是利用闭包保存定时器状态,并在每次触发时清空重来。
function debounce(fn, delay = 300, immediate = false) {
let timer = null;
return function (...args) {
const context = this;
// 清除旧定时器是关键,保证只记录最后一次操作
if (timer) clearTimeout(timer);
if (immediate) {
// 立即执行模式:首次触发直接执行,后续忽略
if (!timer) fn.apply(context, args);
timer = setTimeout(() => { timer = null; }, delay);
} else {
// 延迟执行模式:等用户停手才执行
timer = setTimeout(() => { fn.apply(context, args); }, delay);
}
};
}
注意细节,展开运算符接收参数并原样传递,apply 牢牢锁住调用者的 this 指向。对于需要主动中断的场景,可以在返回函数上挂载 cancel 方法,方便组件卸载或网络拦截时手动清理残留定时器。
节流:控制出水节奏
如果说防抖是“等人齐再走”,那节流就是“水龙头定时滴水”。不管你怎么拧龙头,水流只能按固定频率出来。在滚动加载或高频点击场景中,用防抖会导致功能失效,必须换成节流。这里推荐时间戳对比法,比纯定时器更精准,还能有效防止事件堆积。
function throttle(fn, interval = 300) {
let lastTime = 0;
return function (...args) {
const context = this;
const now = Date.now();
// 只有距离上次执行超过阈值时才放行
if (now - lastTime >= interval) {
lastTime = now;
return fn.apply(context, args);
}
};
}
很多初学者习惯用 setTimeout 嵌套实现节流,结果遇到网络延迟或UI卡顿,第二次请求会被卡在任务队列里迟迟不发送。时间戳方案直接读取当前毫秒数,确保指令严格按照间隔发出,执行频率绝对可控,不会因浏览器调度延迟产生漂移。
实战搭配与避坑指南
封装完毕不等于万事大吉,搭配业务场景才能发挥最大效用。输入框联想查询、拖拽生成校验提示,这类“求稳不准误伤”的操作首选防抖;窗口缩放自适应布局、滚动条无限加载数据,以及防重复点击按钮,则必须用节流压住节奏。
现代开发中,如果包装的函数本身基于 async/await,别忘了在节流与防抖的调用处显式 return。否则父级无法捕获异常或等待结果,调试时会以为逻辑被吞掉了。建议在函数末尾统一补上 return fn.apply(context, args),保持调用链路的完整性。偶尔给封装函数追加 dispose 清理钩子,也能让内存管理更干净。
写在后面
防抖和节流不是炫技的语法糖,而是前端性能优化的基本功。把这两套模板装进自己的工具库,遇到高频事件不再手忙脚乱。写出稳定、可预测的代码,才是开发者真正的底气。


还没有评论,来说两句吧...