JavaScript实现防抖、节流【带思路】
目录
前言
防抖
节流
lodash
lodash的防抖
lodash的节流
前言
防抖与节流一直是JavaScript异步处理中的经典问题,尽管现代已有许多库,我们可以很方便的使用它们,但是理解它们背后的原理还是十分重要的。
那么“防抖”与“节流”分别是什么意思呢?
- 防抖:用户一段时间内频繁执行同一种操作,只执行最后一次。如:用户1s内连续点击“下载按钮”100次,最终只执行一次下载
- 节流:用户一段时间内频繁执行同一种操作,但按照规定的最大时间限制规定期执行。如:用户10s内连续点击“更新按钮”100次,但最大时间限制为1s,最终只执行10次更新
简而言之,不管是“防抖”还是“节流”,它们使用的背景都是基于“一段时间内频繁执行”
防抖
防抖要满足:“对于一段时间内频繁的操作,只执行最后一次操作”,很容易想到使用“setTimeout”构建一个定时器,当一段时间过去后,最终只会执行一次setTimeout内的函数体。
那么当定时器在计时状态下,用户点击操作,计时器就要重新计时,因为这个时候可以理解为“一段时间”还没有过去,因为用户还在点击。“一段时间”过去的结束标志应该是“用户长时间没有执行点击操作”
因此我们使用“闭包”存储这个计时器,当再次点击时clearTimeout并且重新计时即可
<body><input id="input" type="text" value="0"><button id="button">增加</button><script>const input = document.getElementById('input');const button = document.getElementById('button');// 防抖函数const debounce = (func, delay) => {let timer = null;const debounced = function (...args){const context = this;if (timer) {clearTimeout(timer);}timer = setTimeout(() => {func.apply(context, args);}, delay);}return debounced;}button.addEventListener('click', debounce(() => {input.value = parseInt(input.value) + 1;}, 500));</script>
</body>
效果:
你可能会有这种需求:“前1次点击操作需要进行防抖,后面就随客户玩去吧!”,那么我们可以给防抖方法添加cancel方法(只需要让delay为0即可),用来手动停止防抖
<body><input id="input" type="text" value="0"><button id="button">增加</button><script>const input = document.getElementById('input');const button = document.getElementById('button');// 防抖函数const debounce = (func, delay) => {let timer = null;const debounced = function (...args){const context = this;if (timer) {clearTimeout(timer);}timer = setTimeout(() => {func.apply(context, args);}, delay);}debounced.cancel = function() {clearTimeout(timer);timer = null;delay = 0;}return debounced;}const clickDebounce = debounce(() => {input.value = parseInt(input.value) + 1;if (input.value >= 1) {clickDebounce.cancel();}}, 500);button.addEventListener('click', clickDebounce);</script>
</body>
效果:
节流
节流要满足:“对于一段时间内频繁的操作,总会按照给定的最大时间限制定期执行操作”,我们仍然可以使用setTimeout来解决,如果用户的某一次点击中,timer的状态不为空,说明此时在一个执行周期内,直接return忽略即可。
<body><input id="input" type="text" value="0"><button id="button">增加</button><script>const input = document.getElementById('input');const button = document.getElementById('button');// 节流函数const throttle = (func, delay) => {let timer = null;const throttled = function (...args){const context = this;if (timer) {return;}timer = setTimeout(() => {func.apply(context, args);timer = null;}, delay);}return throttled;}button.addEventListener('click', throttle(() => {input.value = parseInt(input.value) + 1;}, 1000));</script>
</body>
效果:
你可能会有这种需求:“前1次点击操作需要进行节流,后面就随客户玩去吧!”,那么我们可以给节流方法添加cancel方法(只需要让delay为0即可),用来手动停止节流
<body><input id="input" type="text" value="0"><button id="button">增加</button><script>const input = document.getElementById('input');const button = document.getElementById('button');// 节流函数const throttle = (func, delay) => {let timer = null;const throttled = function (...args){const context = this;if (timer) {return;}timer = setTimeout(() => {func.apply(context, args);timer = null;}, delay);}throttled.cancel = function() {clearTimeout(timer);timer = null;delay = 0;}return throttled;}const clickThrottle = throttle(() => {input.value = parseInt(input.value) + 1;if (input.value >= 1) {clickThrottle.cancel();}}, 1000);button.addEventListener('click', clickThrottle);</script>
</body>
效果:
lodash
现在我们已初步实现了防抖和节流,但是当我们理解原理之后,我更推荐大家使用已有的库调用比较成熟的方法
Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。
其中就有debounce防抖和throttle节流方法供我们使用,具体可以参考官网:
Lodash 简介 | Lodash中文文档 | Lodash中文网
lodash的防抖
语法:
_.debounce(func, [wait=0], [options=])
以下是官方的解释:
<body><input id="input" type="text" value="0"><button id="button">增加</button><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script><script>const input = document.getElementById('input');const button = document.getElementById('button');button.addEventListener('click', _.debounce(() => {input.value = parseInt(input.value) + 1;}, 500));</script>
</body>
lodash的节流
语法:
_.throttle(func, [wait=0], [options=])
以下是官方的解释:
<body><input id="input" type="text" value="0"><button id="button">增加</button><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script><script>const input = document.getElementById('input');const button = document.getElementById('button');button.addEventListener('click', _.throttle(() => {input.value = parseInt(input.value) + 1;}, 1000));</script>
</body>