概念理解
打工的阿飞
为了更形象地解释防抖(Debounce)这一概念, 可以想象我们的一位朋友阿飞
阿飞到餐厅打工当服务生, 帮助顾客点单是他的主要工作
在顾客点单结束时, 阿飞会询问顾客, "请问还需要其它菜品吗?", 只有当顾客确认不再需要其它的菜品, 阿飞才会把顾客的点单传达到后厨
相当正常的流程, 不是吗? 但阿飞这样的点单流程正是应用了防抖后的结果
那没有应用防抖的点单流程是什么样的呢? 那大家就有的忙活了:
顾客每点一道菜, 阿飞就需要把这一信息传达到后厨(后厨大概率会被点单给淹没吧); 而且如果在阿飞向后厨传递信息的路上, 顾客又点了一道菜, 这时候就需要其它服务生来向后厨传递这一信息
那要是点菜的顾客是一位相声演员, 心血来潮来了一段"报菜名", 那就算是十个阿飞也顶不住啊...
此外, 这还是在点的每一道菜都是顾客确实需要的情况下. 要是顾客点着点着, 觉得菜品点得太多, 决定最先点的几道菜不要了, 那可真就是乱了套了
流程比较
通过比较两种点单流程我们可以发现, 二者最大的不同在于: 有没有确认当前点单就是用户的最终点单, 因为只有最终点单是应该传递给后厨的
类比到前端开发中,
例如我们需要根据用户对浏览器窗口大小的改变, 即 resize 事件,
进行相关处理, 这时需要的往往是用户最终的改变结果,
而改变过程中对 resize 事件的触发往往是不需要处理的
一种实现
既然防抖处理可以有效地降低高频触发任务的负荷, 那么我们可以如何实现它呢?
我认为, 关键点就在于添加对事件本次触发为最终触发的确认, 就是阿飞对点单顾客说的那句"请问还需要其它菜品吗?"
而在程序设计中, 对该确认的一种实现思路是: 在事件触发后添加一段等待时间, 若在等待时间内事件没有再触发, 则确认本次触发为最终触发, 执行相应的处理函数; 若在等待时间内该事件再次触发, 则刷新等待时间, 重新判断
这种实现思路就好比顾客不再点单后, 阿飞不会立即去后厨, 而是先等一会, 如果等了一会之后顾客也没有再点单了, 阿飞才把订单送到后厨
/**
* 为函数设置防抖
*
* @param {Function} func 将要添加防抖的函数
* @param {number} delay 等待时间
* @return {Function} 添加防抖后的函数
*/
const debounce = (func, delay) => {
let inDebounce;
return function(...args) {
const context = this;
// 若在等待时间内函数再次被调用, 即对应事件发生变化, 则刷新重新判断
clearTimeout(inDebounce);
inDebounce = setTimeout(
() => Reflect.apply(func, context, args),
delay
);
}
}
测试
我们可以基于 onscroll 来粗略展示一下防抖的效果
-
HTML<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>debounce</title> </head> <body> <div id="demo" style="height: 1000px"></div> <script src="./debounce.js"></script> </body> </html> -
JavaScript/** * 为函数设置防抖 * * @param {Function} func 将要添加防抖的函数 * @param {number} delay 等待时间 * @return {Function} 添加防抖后的函数 */ const debounce = (func, delay) => { let inDebounce; return function (...args) { const context = this; // 若在等待时间内函数再次被调用, 即对应事件发生变化, 则刷新重新判断 clearTimeout(inDebounce); inDebounce = setTimeout( () => Reflect.apply(func, context, args), delay ); } } const demo = document.getElementById("demo"); function toDebounce() { demo.innerHTML += ("上下滑动对应回调函数被执行了<br>"); } // window.onscroll = toDebounce; window.onscroll = debounce(toDebounce, 1000); -
添加防抖前
-
添加防抖后
从上述两张 Gif 图中, 我们可以看出: 在添加防抖之前,
onscroll 对应的函数将会随着页面的上下滑动被执行多次;
而在添加防抖之后, 只有当上下滑动停止, 并经过等待时间也未再次触发后,
对应的函数才会被执行
总结
防抖可以应用于优化高频次触发的事件, 除了上文中提到的改变浏览器窗口大小 resize 和上下滑动浏览器窗口 scroll, 还有自动保存等应用
以上是我对防抖(Debounce)的理解, 如果文章中存在问题或是大家有更好的理解, 欢迎大家指出和交流:)