原文:函数防抖和节流;
序言:
我们在平时开发的时候,会有很多场景会频繁触发事件,比如说搜索框实时发请求,onmousemove, resize, onscroll等等,有些时候,我们并不能或者不想频繁触发事件,咋办呢?这时候就应该用到函数防抖和函数节流了!
准备材料:
<div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div> <script> let num = 1; let content = document.getElementById(‘content‘); function count() { content.innerHTML = num++; }; content.onmousemove = count; </script>
这段代码, 在灰色区域内鼠标随便移动,就会持续触发 count() 函数,导致的效果如下:
接下来我们通过防抖和节流限制频繁操作。
函数防抖(debounce)
短时间内多次触发同一事件,只执行最后一次,或者只执行最开始的一次,中间的不执行。
// 非立即执行版 function debounce(func, wait) { let timer; return function() { let context = this; // 注意 this 指向 let args = arguments; // arguments中存着e if (timer) clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args) }, wait) } }
我们是这样使用的:
content.onmousemove = debounce(count,1000);
非立即执行版的意思是触发事件后函数不会立即执行,而是在 n 秒后执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。效果如下:
// 立即执行版 function debounce(func, wait) { let timer; return function() { let context = this; // 这边的 this 指向谁? let args = arguments; // arguments中存着e if (timer) clearTimeout(timer); let callNow = !timer; timer = setTimeout(() => { timer = null; }, wait) if (callNow) func.apply(context, args); } }
立即执行版的意思是触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果。用法同上,效果如下:
// 合成版 /** * @desc 函数防抖 * @param func 目标函数 * @param wait 延迟执行毫秒数 * @param immediate true - 立即执行, false - 延迟执行 */ function debounce(func, wait, immediate) { let timer; return function() { let context = this, args = arguments; if (timer) clearTimeout(timer); if (immediate) { let callNow = !timer; timer = setTimeout(() => { timer = null; }, wait); if (callNow) func.apply(context, args); } else { timer = setTimeout(() => { func.apply }, wait) } } }
节流(throttle)
指连续触发事件但是在 n 秒中只执行一次函数。即 2n 秒内执行 2 次... 。节流如字面意思,会稀释函数的执行频率。
同样有两个版本,时间戳和定时器版。
// 时间戳版 function throttle(func, wait) { let previous = 0; return function() { let now = Date.now(); let context = this; let args = arguments; if (now - previous > wait) { func.apply(context, args); previous = now; } } }
使用方式如下:
content.onmousemove = throttle(count,1000);
效果如下:
可以看到,在持续触发事件的过程中,函数会立即执行,并且每 1s 执行一次。
// 定时器版 function throttle(func, wait) { let timeout; return function() { let context = this; let args = arguments; if (!timeout) { timeout = setTimeout(() => { timeout = null; func.apply(context, args) }, wait) } } }
用法同上,效果如下:
可以看到,在持续触发事件的过程中,函数不会立即执行,并且每 1s 执行一次,在停止触发事件后,函数还会再执行一次。
我们应该可以很容易的发现,其实时间戳版和定时器版的节流函数的区别就是,时间戳版的函数触发是在时间段内开始的时候,而定时器版的函数触发是在时间段内结束的时候。
同样地,我们也可以将时间戳版和定时器版的节流函数结合起来,实现双剑合璧版的节流函数。
/** * @desc 函数节流 * @param func 函数 * @param wait 延迟执行毫秒数 * @param type 1 表时间戳版,2 表定时器版 */ function throttle(func, wait ,type) { if (type === 1) { let previous = 0; } else if (type === 2) { let timeout; } return function() { let context = this; let args = arguments; if (type === 1) { let now = Date.now(); if (now - previous > wait) { func.apply(context, args); previous = now; } } else if (type === 2) { if (!timeout) { timeout = setTimeout(() => { timeout = null; func.apply(context, args) }, wait) } } } }
原文地址:https://www.cnblogs.com/cc-freiheit/p/10827372.html