网站关键词的优化在哪做,免费网站软件免费下载安装,wordpress历史版本号,减肥产品网站模板我对防抖#xff08;Debounce#xff09;的一点理解与实践这篇文章主要是我在项目中使用防抖过程中的一些总结#xff0c;只代表个人理解#xff0c;如果有不严谨或可以优化的地方#xff0c;欢迎指出和讨论。一、防抖的概念
防抖#xff08;Debounce#xff09; #…我对防抖Debounce的一点理解与实践这篇文章主要是我在项目中使用防抖过程中的一些总结只代表个人理解如果有不严谨或可以优化的地方欢迎指出和讨论。一、防抖的概念防抖Debounce简单来说就是在短时间内多次触发同一个函数时只让它在“合适的时机”执行一次。常见的两种形式尾触发停止触发一段时间后才执行立即执行第一次触发立刻执行随后一段时间内不再执行防抖本身并不复杂真正复杂的地方在于什么时候该用哪一种以及实现细节是否可靠。二、为什么要做防抖重点在实际项目中高频触发几乎无处不在用户快速点击按钮表单多次提交输入框实时搜索如果不加控制往往会带来一些问题接口被重复调用产生重复副作用多次提交、多次弹窗状态错乱难以维护防抖解决的核心问题是函数触发频率过高而这些触发中只有一部分是真正“有意义”的。通过防抖我们可以在函数入口处统一控制执行频率而不是在函数内部到处加判断。2.1 除了防抖还有其它方案吗简单带过实际开发中也经常能看到一些方案loading 状态控制页面多个按钮loading按钮过多然后二次封装按钮组件接下来先按照我个人的理解来说一下还是防抖。三、基础版本防抖实现3.1 最基础的防抖写法functiondebounce(func,wait200){lettimeoutnullreturnfunction(...args){clearTimeout(timeout)timeoutsetTimeout((){func.apply(this,args)},wait)}}这个版本属于最经典的尾触发防抖多次触发只会执行最后一次3.2 普通函数与箭头函数的区别防抖实现中经常会看到两种写法constcontentthissetTimeout(function(){func.apply(content,args)},wait)以及setTimeout((){func.apply(this,args)},wait)这两种写法的核心区别在于this的绑定机制不同普通函数的this是运行时动态绑定的由函数的调用方式决定在setTimeout等场景中容易发生this丢失。箭头函数不会创建自己的this它的this在定义时就已经确定始终指向外层作用域的this因此非常适合用于定时器和回调函数中。因此在防抖中如果使用普通函数往往需要额外保存 this而使用箭头函数可以让代码更简洁。然后具体的情况需要具体分析这里不展开细说 this 的规则。而且里面还涉及到了applycallbind等知识四、立即执行版防抖4.1 为什么需要立即执行版普通防抖有一个明显特点第一次触发不会立即执行在一些场景下这并不是我们想要的行为例如提交按钮登录、支付等关键操作这类场景下更合理的预期是第一次点击立刻执行但在短时间内禁止再次触发。这就是立即执行版防抖存在的意义。4.2 立即执行版完整实现functiondebounce(func,wait200,immediatefalse){lettimeoutnullreturnfunction(...args){// 是否需要立即执行第一次触发constcallNowimmediate!timeout// 清除之前的定时器if(timeout)clearTimeout(timeout)// 设置新的定时器用于冷却期结束timeoutsetTimeout((){// 冷却结束重置状态timeoutnull// 非立即执行模式走尾触发if(!immediate){func.apply(this,args)}},wait)// 立即执行只会执行一次if(callNow){func.apply(this,args)}}}4.3 这一版的核心思路在这个实现中timeout不只是一个定时器它同时承担了“是否处于冷却期” 的状态标记constcallNowimmediate!timeout!timeout表示当前不在冷却期只允许第一次触发立即执行当定时器结束后timeoutnull表示冷却期结束允许下一次立即执行。五、结合源码理解实现逻辑在理解了立即执行版防抖的实现后再回看 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 nulltimeout null;这一步表示冷却期结束为下一次立即执行创造条件。如果不重置immediate只会生效一次。立即执行的关键逻辑var callNow !timeout; timeout setTimeout(later, wait); if (callNow) func.apply(this, args);这三行完成了三件事判断是否第一次触发立刻进入冷却期只在第一次触发时立即执行后续触发只会刷新定时器不会重复执行。为什么源码不用this而是传contextlater是普通函数this不可靠。因此 Underscore 选择显式传递context保证this指向稳定这是典型的库级写法。核心结论防抖并不依赖复杂 API本质只有两点定时器状态控制是否处于冷却期立即执行与否本质区别只是函数是在冷却期开始时执行还是在冷却期结束时执行。总结防抖本身并不难真正容易出问题的是使用场景选错this 指向理解不清状态与执行职责混在一起这篇文章更多是我个人在项目中的一些理解与总结如果你在实践中有不同的经验或看法也非常欢迎交流。