JavaScript防抖与节流函数深度解析及源码实现
本文深入解析JavaScript中防抖(debounce)与节流(throttle)的核心概念、应用场景及源码实现。通过对比电子/流体领域的起源,阐明两者在限制高频事件触发上的差异。文章详细列举了搜索框、滚动监听等典型场景,并提供了包含参数优化、取消功能及立即执行等高级特性的完整代码实现,帮助开发者彻底掌握这两个前端性能优化的关键工具。
防抖与节流函数的核心解析
理解防抖和节流的基本概念
防抖与节流的概念并非起源于软件工程,其中防抖概念源自电子元件领域,而节流则源于流体动力学中的流动现象。
- JavaScript是一种事件驱动型语言,大量操作会触发相应事件并加入事件队列进行处理。
- 对于某些高频触发的事件处理,若不加限制会造成显著的性能损耗,此时引入防抖和节流机制可有效限制事件的频繁触发。
目前,防抖和节流函数已是前端实际开发中至关重要的工具,同时也是面试中高频考察的知识点。
然而,许多前端开发者在面对这两个概念时感到困惑:
- 部分开发者无法准确区分防抖与节流的区别(这也是面试常见考点);
- 部分开发者虽能区分,却不清楚具体应用场景;
- 部分开发者仅依赖第三方库使用,却不明其内部原理,更无法独立编写。
深入认识防抖(debounce)函数
我们可以通过图示直观理解其工作流程:
- 当事件触发时,响应函数并不会立即执行,而是启动一个定时器等待指定时间;
- 若在等待期间事件被密集触发,函数的执行会被不断推迟;
- 只有当等待了一段时间且期间没有新事件触发时,响应函数才会真正执行。

防抖机制的应用场景十分广泛:
- 输入框中频繁输入内容,如搜索关键词或提交表单信息;
- 用户频繁点击按钮以触发特定事件;
- 监听浏览器滚动事件,以执行特定的页面操作;
- 监听浏览器窗口大小变化的resize事件。
防抖函数的实际案例演示
我们都曾遇到过这样的场景:在搜索框中输入想要查找的内容。
例如,想要搜索“MacBook”。
- 当输入字母“m”时,为了提供更好的用户体验,系统通常会显示联想内容,这些内容存储在服务器端,因此需要发起一次网络请求;
- 当继续输入“ma”时,系统再次发送网络请求;
- 那么,输入完整的“macbook”一词,一共需要发送7次网络请求;
- 这极大地消耗了系统性能,无论是前端的事件处理开销,还是对服务器造成的压力;
然而,我们需要发送这么多次网络请求吗?
- 显然不需要,正确的做法是在合适的时机再发送网络请求;
- 例如,如果用户快速输入“macbook”,则只需发送一次网络请求;
- 又或者,如果用户输入“m”后停顿思考,此时“m”确实应该发送一次网络请求;
- 也就是说,我们应该监听用户在特定时间(如500ms内)是否再次触发事件,若未再次触发,则发送网络请求;
这就是防抖的操作逻辑:只有在指定时间内没有再次触发函数时,才真正调用该函数。
深入认识节流(throttle)函数
我们可以通过图示来理解节流的过程:
- 当事件触发时,会执行该事件的响应函数;
- 如果事件被频繁触发,节流函数会按照固定的频率来执行响应函数;
- 无论中间触发多少次事件,函数的执行频率始终保持固定。

节流函数的典型应用场景包括:
- 监听页面的滚动事件;
- 监听鼠标移动事件;
- 用户频繁点击按钮的操作;
- 游戏中的某些机制设计。
节流函数的具体应用场景分析
许多人玩过类似“飞机大战”的游戏。
在飞机大战游戏中,按下空格键会发射子弹:
- 许多此类游戏设定,即使玩家按键频率非常快,子弹也会保持一定的发射频率;
- 例如设定为1秒只能发射一次,即使用户在1秒内按下了10次,子弹也会保持每秒发射一颗的频率;
- 此时事件虽然触发了10次,但响应函数只触发了一次。

自定义防抖和节流函数的实现思路
我们按照以下思路来实现:
防抖基本功能实现:确保具备防抖效果。
- 优化一:优化参数传递和this指向问题。
- 优化二:增加取消操作功能(支持cancel)。
- 优化三:优化立即执行效果(支持第一次立即执行)。
- 优化四:优化返回值处理。
function debounce(fn,delay,immediate=false,resultCallback){
let timer=null
// console.log(this)//window
// 定义控制立即执行的变量,false表示没有执行过
let isInvoke=false
// 真正的处理函数
function _debounce(...args){
// 取消事件执行操作
if(timer) clearTimeout(timer)
// console.log(this)//element元素
if(immediate&&!isInvoke){
const result=fn.apply(this,args)
resultCallback(result)
isInvoke=true
}else{
// 延迟执行
timer=setTimeout(()=>{
const result=fn.apply(this,args)
resultCallback(result)
timer=null
isInvoke=false
},delay)
}
}
// 封装取消请求
_debounce.cancel=function(){
if(timer) clearTimeout(timer)
timer=null
isInvoke=false
}
return _debounce
}
我们按照以下思路来实现节流函数:
节流函数的基本实现:确保具备节流效果。
- 优化一:确保节流最后一次触发也能执行。
- 优化二:增加取消功能(支持cancel)。
- 优化三:解决返回值问题。
function throttle(fn,interval,options={leading:true,trailing:false}){
let lastTime=0
const {leading,trailing,resultCallback}=options
let timer=null
function _throttle(...args){
const nowTime=new Date().getTime()
// leading优化
if(!leading&&!lastTime) lastTime=nowTime
let remainTime=interval-(nowTime-lastTime)
if(remainTime<=0){
if(timer){
clearTimeout(timer)
timer=null
}
// 参数优化
const result=fn.apply(this,args)
if(resultCallback) resultCallback(result)
lastTime=nowTime
return
}
// 优化trailing
if(!timer&&trailing){
timer=setTimeout(()=>{
// 参数优化
const result=fn.apply(this,args)
if(resultCallback) resultCallback(result)
timer=null
lastTime=!leading?0:new Date().getTime()
},remainTime)
}
}
_throttle.cancel=function(){
if(timer) clearTimeout(timer)
timer = null
lastTime = 0
}
return _throttle
}
总结
本文到此结束,希望能对您有所帮助,也欢迎您关注本站以获取更多相关内容!
JS动态构建复杂嵌套JSON对象的方法
2026-11-05 15:41:52
JS负数取余为什么是负数?详解余数与模运算区别
2026-11-05 13:34:41
JS 解压出现乱码或报错怎么解决?
2026-11-05 12:32:38
JS 中 startsWith、indexOf 与正则判断字符串前缀的对比
2026-11-04 22:19:43
JS解析Excel后怎么将数据转换为JSON格式
2026-11-04 20:58:28
