news 2026/5/19 17:43:10

我对防抖(Debounce)的一点理解与实践:从基础到立即执行

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
我对防抖(Debounce)的一点理解与实践:从基础到立即执行

我对防抖(Debounce)的一点理解与实践

这篇文章主要是我在项目中使用防抖过程中的一些总结,只代表个人理解,如果有不严谨或可以优化的地方,欢迎指出和讨论。


一、防抖的概念

防抖(Debounce),简单来说就是:

在短时间内多次触发同一个函数时,只让它在“合适的时机”执行一次。

常见的两种形式:

  • 尾触发:停止触发一段时间后才执行
  • 立即执行:第一次触发立刻执行,随后一段时间内不再执行

防抖本身并不复杂,真正复杂的地方在于:什么时候该用哪一种,以及实现细节是否可靠。


二、为什么要做防抖(重点)

在实际项目中,高频触发几乎无处不在:

  • 用户快速点击按钮
  • 表单多次提交
  • 输入框实时搜索

如果不加控制,往往会带来一些问题:

  • 接口被重复调用
  • 产生重复副作用(多次提交、多次弹窗)
  • 状态错乱,难以维护

防抖解决的核心问题是:

函数触发频率过高,而这些触发中,只有一部分是真正“有意义”的。

通过防抖,我们可以在函数入口处统一控制执行频率,而不是在函数内部到处加判断。


2.1 除了防抖,还有其它方案吗?(简单带过)

实际开发中,也经常能看到一些方案:

  • loading 状态控制

  • 页面多个按钮,loading按钮过多,然后二次封装按钮组件

接下来先按照我个人的理解,来说一下还是防抖。


三、基础版本防抖实现

3.1 最基础的防抖写法

functiondebounce(func,wait=200){lettimeout=nullreturnfunction(...args){clearTimeout(timeout)timeout=setTimeout(()=>{func.apply(this,args)},wait)}}

这个版本属于最经典的尾触发防抖

  • 多次触发只会执行最后一次

3.2 普通函数与箭头函数的区别

防抖实现中经常会看到两种写法:

constcontent=thissetTimeout(function(){func.apply(content,args)},wait)

以及:

setTimeout(()=>{func.apply(this,args)},wait)

这两种写法的核心区别在于this的绑定机制不同

  • 普通函数的this是运行时动态绑定的,由函数的调用方式决定,在setTimeout等场景中容易发生this丢失。
  • 箭头函数不会创建自己的this,它的this在定义时就已经确定,始终指向外层作用域的this,因此非常适合用于定时器和回调函数中。

因此在防抖中,如果使用普通函数,往往需要额外保存 this;
而使用箭头函数,可以让代码更简洁。然后具体的情况需要具体分析

这里不展开细说 this 的规则。
而且里面还涉及到了apply,call,bind等知识


四、立即执行版防抖

4.1 为什么需要立即执行版

普通防抖有一个明显特点:

  • 第一次触发不会立即执行

在一些场景下,这并不是我们想要的行为,例如:

  • 提交按钮
  • 登录、支付等关键操作

这类场景下,更合理的预期是:

第一次点击立刻执行,但在短时间内禁止再次触发。

这就是立即执行版防抖存在的意义。


4.2 立即执行版完整实现

functiondebounce(func,wait=200,immediate=false){lettimeout=nullreturnfunction(...args){// 是否需要立即执行(第一次触发)constcallNow=immediate&&!timeout// 清除之前的定时器if(timeout)clearTimeout(timeout)// 设置新的定时器,用于冷却期结束timeout=setTimeout(()=>{// 冷却结束,重置状态timeout=null// 非立即执行模式,走尾触发if(!immediate){func.apply(this,args)}},wait)// 立即执行(只会执行一次)if(callNow){func.apply(this,args)}}}

4.3 这一版的核心思路

在这个实现中:

  • timeout不只是一个定时器
  • 它同时承担了“是否处于冷却期” 的状态标记
constcallNow=immediate&&!timeout
  • !timeout表示当前不在冷却期
  • 只允许第一次触发立即执行

当定时器结束后:

timeout=null

表示冷却期结束,允许下一次立即执行。


五、结合源码理解实现逻辑

在理解了立即执行版防抖的实现后,再回看 Underscore.js 的_.debounce源码,其实可以发现:核心思想完全一致,只是写法更偏工程化。

_.debounce = function(func, wait, immediate) { var timeout, result; var later = function(context, args) { timeout = null; if (args) result = func.apply(context, args); }; var debounced = function(...args) { if (timeout) clearTimeout(timeout); if (immediate) { var callNow = !timeout; timeout = setTimeout(later, wait); if (callNow) result = func.apply(this, args); } else { timeout = setTimeout(() => later(this, args), wait); } return result; }; return debounced; };

timeout是防抖的核心状态

timeout不只是定时器 ID,更是是否处于冷却期的状态标识

  • timeout === null:不在冷却期
  • timeout !== null:正在防抖中

立即执行模式正是通过!timeout来判断“是否第一次触发”。


为什么定时器里要timeout = null

timeout = null;

这一步表示冷却期结束,为下一次立即执行创造条件。
如果不重置,immediate只会生效一次。


立即执行的关键逻辑

var callNow = !timeout; timeout = setTimeout(later, wait); if (callNow) func.apply(this, args);

这三行完成了三件事:

  1. 判断是否第一次触发
  2. 立刻进入冷却期
  3. 只在第一次触发时立即执行

后续触发只会刷新定时器,不会重复执行。


为什么源码不用this,而是传context

later是普通函数,this不可靠。
因此 Underscore 选择显式传递context,保证this指向稳定,这是典型的库级写法。


核心结论

防抖并不依赖复杂 API,本质只有两点:

  • 定时器
  • 状态控制(是否处于冷却期)

立即执行与否,本质区别只是:

函数是在冷却期开始时执行,还是在冷却期结束时执行。

总结

防抖本身并不难,真正容易出问题的是:

  • 使用场景选错
  • this 指向理解不清
  • 状态与执行职责混在一起

这篇文章更多是我个人在项目中的一些理解与总结,
如果你在实践中有不同的经验或看法,也非常欢迎交流。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/12 16:31:47

重构 Flutter 状态管理:从 Provider 到 Riverpod 2.0 的无痛迁移与性能飞跃

欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。 在 Flutter 开发的迭代长河中,状态管理始终是绕不开的核心命题。Provider 曾凭借简洁的 API 和低学习成本成为主流选择,…

作者头像 李华
网站建设 2026/5/13 0:34:52

AI大模型之Agent,RAG,LangChain(三)

前面分享了项目的大致流程,这篇主要分享一下LangChain这个框架的个人简单理解.看懂这篇必须要看我之前发的二,这样便于更好的理解这个框架.一.简述1.什么是LangChain简单来说,LangChain是LLMs的开发框架,他为不同的LLMs提供统一的接口,并且把和LLMs相关的内部组件连接在一起.2.…

作者头像 李华
网站建设 2026/5/18 15:49:58

css3如何引入外部字体

如果需要外部字体,电脑上没有,这时候可以用css3上新引入的font-face属性它的语法格式是font-face {font-family:自定义字体名称src:url(字体路径);}比如需要使用叫字体家AI北京长城体.ttf的字体,代码如下font-face {/*定义字体的名称*/font-f…

作者头像 李华
网站建设 2026/5/19 6:01:15

OkDownload入门指南:如何在5分钟内搭建你的第一个高效下载引擎

OkDownload入门指南:如何在5分钟内搭建你的第一个高效下载引擎 【免费下载链接】okdownload A Reliable, Flexible, Fast and Powerful download engine. 项目地址: https://gitcode.com/gh_mirrors/ok/okdownload 想要构建一个可靠、灵活且高效的下载系统吗…

作者头像 李华
网站建设 2026/5/17 4:46:10

权威榜单发布:浙江亿企邦领衔外贸网站引流推广行业

随着外贸行业数字化转型的深入,企业对专业化、多渠道、智能化的海外营销服务的需求日益迫切。为帮助广大外贸企业甄选优质服务伙伴,基于企业综合服务能力、技术创新水平、行业口碑与市场表现等多维度评估,现正式发布2025年度外贸网站引流推广…

作者头像 李华
网站建设 2026/5/19 14:33:56

新能源电动汽车VCU、Hil、BMS及硬件在环仿真建模说明书

新能源电动汽车VCUhilBMShil硬件在环仿真 文件包含电动汽车整车建模说明书,模型包含驾驶员模块,仪表模块,BCU整车控制器模块,MCU电机模块,TCU变速箱模块,减速器模块,BMS电池管理模块&#xff0c…

作者头像 李华