为什么要防抖和节流?
防止高频次的事件触发造成回调函数来不及执行(也就是回调函数的执行跟不上频繁的事件触发)。
例如当实现一个函数:当鼠标经过div时就会输出对应的坐标。这仅仅是输出坐标可能还能跟得上,但是我们可以把此回调函数想成复杂函数,来想下防抖和节流…
防抖
可能更想要的功能是当鼠标停留在某个地方时才会输出坐标。那么怎么知道鼠标停下了呢?可以认为当过一段时间(比如10ms)仍然没有触发事件(鼠标没有移动)就可以认为在停留了。一段时间需要依赖setTimeout这个函数:setTimeout(fn,10000)
。代表10ms以后再输出坐标。但是如果10ms以内又移动了怎么办呢?只好重新计时啦。
function debounce(fn,delay){
let timer = null //借助闭包,因为是闭包,所以还是上次的timer
return function() {
if(timer){
clearTimeout(timer) //进入该分支语句,说明当前正在一个计时过程中,并且又触发了相同事件。所以要取消当前的计时。
timer = setTimeout(fn,delay) //重新开始计时
}else{
timer = setTimeout(fn,delay) // 进入该分支说明当前并没有在计时,那么就开始一个计时
}
}
}
//使用
var box.onmousemove=debounce(function(e){box.innerHTML='坐标:'+e.clientX+','+e.clientY},1000);
节流
如果一个人在盒子内不断移动鼠标,那么就不会输出坐标。但是有时候会希望偶尔会出现一次。这里就用到了节流,节流的意思是一段时间内只执行一次。持续触发,并不会执行,但是到时间了就会执行。如果持续触发的话,没到时间也不能去执行
这里也可以利用闭包:
function throttle(func, delay) {
let run = true
return function () {
if (!run) {
return // 如果开关关闭了,那就直接不执行下边的代码
}
run = false // 持续触发的话,run一直是false,就会停在上边的判断那里
setTimeout(() => {
func.apply(this, arguments)
run = true // 定时器到时间之后,会把开关打开,我们的函数就会被执行
}, delay)
}
}
box.onmousemove = throttle(function(e){box.innerHTML='坐标:'+e.clientX+','+e.clientY},1000);
这里由于是闭包,在下次触发的时候,throttle的活动变量并没有销毁,因此run还是false,会一直停留在return,等delay时间到的时候就会触发回调函数。
间断性触发:
持续性触发:
下次执行是在一次执行完之后的紧接着下次触发之后的delay时间后
参考:https://zhuanlan.zhihu.com/p/72923073
https://segmentfault.com/a/1190000018428170