「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战」
昨天我们简单介绍了函数防抖的概念以及如何手写,详情请看 小陈同学の前端笔记 | 来简单了解下函数防抖 这篇文章。今天我们来看一下限制事件频繁触发的另一种解决方案:节流
什么是节流
所谓节流,就是在频繁触发事件时,每隔一段时间,执行一次函数。
* 节流会稀释函数的执行频率。
以上篇文章中的鼠标移动为例,假设我们规定的时间为 1s ,如果我们频繁地不间断地触发鼠标移动事件,那么每隔 1s 才会执行一次函数。
节流的实现
根据实现的方式划分,目前主流的有两种方式:
- 时间戳版
- 定时器版
时间戳版
使用时间戳,即在触发事件的时候,记录下当前的时间戳,与之前所记录的时间戳做减法,若大于我们人为设置的时间间隔,则执行函数,并更新最新的时间戳;若小于,则不执行函数。
代码如下:
function addNum(){
box.innerHTML = num++
}
function throttleTimeStamp(func,delay) {
let previous = 0 //初始时间戳置为0
return function(){
let now = new Date()
if(now - previous > delay){
func()
previous = now
}
}
}
// 调用
box.onmousemove = throttleTimeStamp(addNum,2000)
我们可以看到当鼠标移入的时候,函数会 立即执行,但在 2s 内只执行一次,2s 后才会执行第二次,这就起到了稀释函数执行的作用。
定时器版
使用定时器,即在触发事件的时候,设置一个定时器,再触发事件时,如果定时器存在,就不执行函数。等到定时器时间到了,再执行函数,清空当下的定时器,并设置下一个定时器。
代码如下:
function throttleTimer(func,delay) {
let timer;
return function(){
if(!timer){
timer = setTimeout(() => {
timer = null
func()
}, delay);
}
}
}
box.onmousemove = throttleTimer(addNum,2000)
这是使用定时器方式的实现效果,我们可以发现,当鼠标移入的时候,函数 不会立即执行,而是等待 2s 后才会执行,且当我们把鼠标移出去的时候,最后一次还是会执行(因为定时器里的函数在离开前并没有调用,等待时间后完成最后一次。)
最终版本
该版本结合了时间戳版的 立即执行与定时器版的 停止触发后再执行一次,并修改了 this 的指向以及解决了 参数的传递 问题。
代码如下:
function throttle(func, wait) {
let timeout, context, args, result;
let previous = 0;
let later = function() {
previous = new Date();
timeout = null;
func.apply(context, args)
};
let throttled = function() {
let now = new Date();
//下次触发 func 剩余的时间
let remaining = wait - (now - previous);
context = this;
args = arguments;
// 如果没有剩余的时间或改了系统时间
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
};
return throttled;
}
可以发现,不仅仅实现了 立即执行函数,而且也做到了停止触发事件后再执行一次函数 。
结语
以上就是节流函数的介绍以及实现的代码,如有纰漏,欢迎指出!
参考文献: github.com/mqyqingfeng…