防抖和节流
一. 防抖
###1. 概念
防抖是指:一个事件被触发的时候,里边的函数不会立即被触发,而是会等待一段时间再触发。
- 举个例子:游戏中的回城就可以认为是防抖,在回城的读秒过程中,如果再次执行回城操作,那么会重新进行读秒,只有整个读秒过程都没有再次执行回城操作,那么等到读秒结束才能成功回城。
###2. 防抖使用场景:
input输入框频繁输入内容时。
频繁点击按钮,触发某个事件。
监听浏览器滚动或鼠标滚动。
3. 防抖讲解
未使用防抖时:
var aa = document.getElementById('ipt')
// 监听键盘按下事件
document.addEventListener('keyup',function(){
console.log(aa.value)
})
效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hcjuihvN-1653654541063)(C:\Users\17679\AppData\Roaming\Typora\typora-user-images\image-20220423164741522.png)]
加上防抖时:
var aa = document.getElementById('ipt')
// 判断当前是否有在有定时器
let t = null
// 监听键盘按下事件
document.addEventListener('keyup',function(){
// 判断当前监听到的事件是否在防抖时间里
if(t != null){
// 不为空则说明上一次触发事件还在时间内,则清除掉当前已记录的时间,重新开始计时
clearTimeout(t)
}
t = setTimeout(function(){
// 业务逻辑
console.log(aa.value)
},1000)
})
效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GTxh62IT-1653654541065)(C:\Users\17679\AppData\Roaming\Typora\typora-user-images\image-20220423165653738.png)]
如此做有一个弊端,也就是当前的业务逻辑和防抖处理交织在一起,且还多了个全局变量 t,一旦业务逻辑多了起来,可读性会非常差,不利于维护。可以将防抖处理封装起来,以求和业务逻辑分开来。
var aa = document.getElementById('ipt')
// 使用debounce防抖函数去约束逻辑业务,由于逻辑业务是一个function函数形式的
// 那么debounce的返回值就应该是一个函数
document.addEventListener('keyup',debounce(function(){
// 是真正的业务逻辑
console.log(aa.value)
},1000))
// 这个fn是为了执行业务逻辑函数
function debounce(fn,delay){
// 判断当前是否有在有定时器
let t = null
// 返回一个函数 (那么debounce的返回值就应该是一个函数)
return function() {
// 判断当前监听到的事件是否在防抖时间里
if(t != null){
// 不为空则说明上一次触发事件还在时间内,则清除掉当前已记录的时间,重新开始计时
clearTimeout(t)
}
t = setTimeout(function(){
// 业务逻辑函数
fn()
},delay)
}
}
如此,可以实现防抖的封装。但是又产生了一个问题,如果采用 this.value 的方式进行,打印结果是undefined。因为匿名函数的this指向window,而window中是没有value的。可以采用call方法来改变this的指向。
二. 节流
###1. 概念
控制执行次数。
- 节流是指当事件触发时,会执行这个事件的响应函数。
- 但是该事件如果被频繁触发,那么节流函数会按照一定的频率来执行函数。
- 举个例子:节流类似于技能cd,不管你按了多少次,必须等到cd结束后才能释放技能。也就是说在如果在cd时间段,不管你触发了几次事件,只会执行一次。只有当下一次cd转换,才会再次执行。
2. 节流应用场景
比如对于一个Button短时间内进行多次点击,可能没有必要触发多次handler,这时候就可以对click的响应函数进行节流处理。
或者一个使用键盘控制的飞机大战的游戏,子弹的射出速度是有限制的,不管你在短时间内触发多少次发射按键,永远只会有一枚子弹被发射。
再或者是在实现无限滚动时,需要去监测内容底部是否已经接近window底部,如果是的话就需要去请求新的内容。
###3. 节流讲解
// 设置一个标志,若为true,则触发滚轮
let flag = true
window.onscroll = function() {
if(flag) {
setTimeout(function(){
console.log('触发了滚轮事件')
},1000)
}
// 事件触发后,立刻将flag设为false
flag = false
}
以上代码可以实现节流,但只能实现一次,下面的代码利用了setTimeout是异步执行的方式。完整实现节流。
// 设置一个标志,若为true,则触发滚轮
let flag = true
window.onscroll = function() {
if(flag) {
setTimeout(function(){
console.log('触发了滚轮事件')
// 当触发事件并开启计时后,将flag设为true
flag = true
},1000)
}
// 事件触发后,立刻将flag设为false
flag = false
}
以下为封装:
// 设置一个标志,若为true,则触发滚轮
window.onscroll = throttle(function () {
console.log('触发了滚轮事件')
}, 1000)
// 封装
function throttle(fn, delay) {
let flag = true
return function () {
if (flag) {
setTimeout(function () {
fn()
// 当触发事件并开启计时后,将flag设为true
flag = true
}, delay)
}
// 事件触发后,立刻将flag设为false
flag = false
}
}
/ 当触发事件并开启计时后,将flag设为true
flag = true
}, delay)
}
// 事件触发后,立刻将flag设为false
flag = false
}
}