Javascript 防抖 节流 控制单位时间内方法执行次数 home 编辑时间 2022/08/17 ![](/api/file/getImage?fileId=62ff0763da74050013012efa) <br><br> ## 前言 和以往不同,没什么原创内容,就是一篇纯笔记,防止自己以后忘 全部来源于学习b站up主 [前端小夏老师](https://space.bilibili.com/8999778) 的这个视频 [函数的防抖和节流 -- JS 原生面试题](https://www.bilibili.com/video/BV1Vy4y1y7tj) 简单解释下防抖节流 都是在单位时间内,限制 `function` 被执行的次数 * 节流好理解,就是点完一次,停止接待1秒,然后在继续接待,停止过程中你随便点,有反应算我输。 * 防抖也不复杂,和上面类似,区别是,必须停够1秒才能再点。如果遇到一直不停的点,那就一直不重置,必须老实停1秒不点,才恢复,和我起床一样,越催越起不来。 <br> 应用范围很广 包括不限于 用户夏姬八乱点按钮(主要是这个) 监听鼠标滚轮(忘了方法名叫啥了) `windows.onresize` 反复提交订单 反复发验证码之类的 等等 <br><br> ## 代码 防抖 ```javascript /** * 防抖核心代码 * 逻辑是 * 第一次点完以后开始计时 * 如果单位时间内点了的话 * 不但无效,而且时间从头计算 * * 例如 * 设置3000毫秒 * 第一次点完如果3秒内再次点击 * 不但不触发任何效果,而且3秒从头开始算 * 如果一直点一直点,就永远无效 */ function debounce(fn, timer) { let timeout = null; return function () { if (timeout) { clearTimeout(timeout); } else { fn.apply(this, arguments); } timeout = setTimeout(() => { timeout = null; }, timer); } } ``` 节流 ```javascript /** * 节流核心代码 * 逻辑是 * 第一次点完以后开始计时 * 如果下次点击时间没达到设定时间 * 则无效,否则有效 * * 例如 * 设置3000毫秒 * 第一次点击完过了1秒点击 判定为无效 立刻记录当前时间 * 过了2秒点击 与上一次时间对比 不足3秒 判定无效 * 过了3秒以上点击 与上次时间对比 足够3秒 判定有效 并立刻记录当前时间 * */ function throttle(fn, delay) { let begin = 0; return function () { const current = new Date().getTime(); if (current - begin > delay) { fn.apply(this, arguments); begin = current; } } } ``` <br><br> ## END 在线测试效果地址 https://tczmh.gitee.io/debounce-and-throttle/ <br><br> ## 补充1 `2022/08/23` 关于用法和其他需求的补充 首先是用法问题 ```html <!-- 这段代码中节流是无法触发的 --> <button onclick="throttle(click0, 1000)">按钮</button> <!-- 而这段代码中就可以触发 --> btn.addEventListener('click', throttle(click0, 1000), false); <!-- 根据我的测试,不推荐放到onclick中触发,如果一定要这样写,就2个方法解决 要么,改写throttle方法,去除return function等,返回值搬到方法里,begin放到函数外面 要么,如下代码申明一个变量,放到onclick中 --> <button onclick="click">按钮</button> const click = throttle(() => { sp0.innerText = ++c0 }, 1000); ``` <br> 另外关于 `onresize` 我之前用过echarts,他默认 `onresize` 会延迟执行, 我需求类似echarts `onresize` 的时候重新渲染canvas、 <br> 如果如果直接把渲染写到 `onresize` 中,会出现改变窗口大小,会高频反复触发,导致性能浪费卡顿,需要限制触发频率,但是和节流和防抖的需求都不一样。比如你拉窗口大小,`onresize` 一共触发300次,希望是执行其中的10次,但最后一次必然要触发,否则渲染的画面就不对了少掉最后一次。 <br> 遇到的问题是,无论是debounce还是throttle 都是减少了执行次数,而不去执行最后一次 也就是窗口变化后,只有第一次和中间某几次,会执行,最终窗口定格了,是最需要执行的,反而不执行了。。。 <br> 解决方法是再写一个ondelay方法 个人觉得会更合适onresize的需求 ```javascript // 就是延迟300秒执行,如果300秒内重复触发,则重置时间,必须停满300秒才执行 function delay(fn, delay) { let timeout = null; return function () { timeout = setTimeout(() => { fn.apply(this, arguments); }, delay); } } // 调用, 这里的resize是你需要延迟执行的function window.onresize = utils.delay(resize, 300); ``` <br> <br> 另外关于 `VUE` 中的防抖和节流问题,下一篇再研究吧 今天累了不想打字了 送人玫瑰,手留余香 赞赏 Wechat Pay Alipay Windows 11 安装 Docker Toolbox 基于 VirtualBox CCS3 立体箱子 页面载入动画