目录
第一章 防抖
1.1 防抖(debounce)简介
1.2 应用场景
1.3 实现思路
1.4 手撕防抖代码
第二章 节流
2.1 节流(throttle)简介
2.2 应用场景
2.3 实现思路
2.4 手撕节流代码(方法:时间戳和计时器)
2.5 时间戳与计时器实现的区别
第三章 总结
第一章 防抖
1.1 防抖(debounce)简介
- 场景:用户在一段时间频繁点击执行某个函数/事件,那么在这段时间,用户点击一次,计时器重新计时,当在这段时间内用户没有触发该函数/事件时,该函数/事件会在这段时间结束时执行。
- 应用示例理解:回城被打断,玩家残血准备回城,需要3s回城成功,但是在这个3s的过程中,玩家又重新点击了回城,导致3s回城重新计算,再等3s。
1.2 应用场景
- 登录、发短信、发送post请求等按钮/事件避免用户点击太快,以致于发送了多次请求,需要防抖,最后一次发送请求即可;
- 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖;
- input输入框获取到value值不需要输入一个字符就获取一次,可以使用防抖,让其输入结束之后再获取其值
- 文本编辑器实时保存,当无任何更改操作一秒后进行保存。
- ……
1.3 实现思路
1.4 手撕防抖代码
function debounce(func,delay) {// 定义一个定时器timerlet timer = nullreturn function() {const that = thisconst args = arguments// 防抖核心:每次触发事件计时器都会重新计时clearTimeout(timer)timer = setTimeout(()=>{func.apply(that,args)},delay)}
}
- 例子:
未调用防抖函数时:用户每在input输入/删除一个值,都会输出一个值;
<input type="text" id="input"><script>let inputDom = document.getElementById('input')//获取输入框的输入内容inputDom.oninput = function(){console.log('this.value',this.value)}</script>
调用防抖函数后:用户在input输入/删除一个值delay之后才,会输出一个值
let inputDom = document.getElementById('input')//func 是执行函数,delay 是事件执行的延迟时间,毫秒function debounce(func,delay) {// 定义一个定时器timerlet timer = nullreturn function() {const that = thisconst args = arguments// 防抖核心:每次触发事件计时器都会重新计时clearTimeout(timer)timer = setTimeout(()=>{func.apply(that,args)},delay)}}function handler() {console.log('this.value',this.value)}inputDom.addEventListener('input', debounce(handler, 1000))
第二章 节流
2.1 节流(throttle)简介
- 场景:用户在一段时间频繁点击执行某个函数/事件,那么在这段时间,用户点击一次/多次(调用事件),都不会影响计时器执行,并且该函数/事件只执行一次。
- 应用示例理解:技能冷却中,玩家在某种情况下使用了闪现这个技能,但是这个技能的冷却时间是120s,在这段时间里,玩家遇到危险,想要再使用闪现这个技能,频繁的点击它,但是并没用,闪现不会执行,计时器依然还在倒计时,等到120s倒计时为0才能再使用一次闪现的技能。
2.2 应用场景
- 窗口调整
- 页面滚动
- 抢购和疯狂点击
- 懒加载获取滚动条的位置
- 鼠标连续不断地触发某事件(如点击),只在规定时间内触发一次
- ……
2.3 实现思路
2.4 手撕节流代码(方法:时间戳和计时器)
// ---------------------节流1:使用时间戳---------------------
//func 是事件处理程序,delay 是事件执行的延迟时间,单位:毫秒
function throttle (func, delay) {//定义初始时间(开始触发事件的时间)let start = 0;return function () {const that = thisconst args = arguments// 获取当前时间戳let cur =Date.now()// 时间间隔大于延迟时间则进入if (cur - start >= delay) {//执行事件处理程序func.apply(that, args);//更新初始时间start = cur}}
}
// ---------------------节流2:使用定时器---------------------
//func 是事件处理程序,delay 是事件执行的延迟时间,单位:毫秒
function throttle(func, delay){// 自定义一个定时器var timer = null;return function(){var that = this;var args = arguments// timer为null表示可以发送请求了,否则还在请求中,不执行事件if(!timer){timer = setTimeout(function(){//执行事件处理程序func.call(that, args)//事件执行完后把定时器清除掉,下次触发事件的时候再设置timer = null;}, delay)} }
}
- 例子:
未调用节流函数时,每点击一次按钮,都会执行一次函数调用:
<button id="button1">发送了节流请求</button>
const button1Dom = document.getElementById('button1')
button1Dom.addEventListener('click',function(){console.log("节流:我发送了消息")
})
使用节流函数之后,频繁的点击发送请求,它会在计时器结束之后再发,这段时间内点击发送一次请求之后,不会再发送请求:
const button1Dom = document.getElementById('button1')
// ---------------------节流1:使用时间戳---------------------
//func 是事件处理程序,delay 是事件执行的延迟时间,单位:毫秒
function throttle (func, delay) {//定义初始时间(开始触发事件的时间)let start = 0;return function () {const that = thisconst args = arguments// 获取当前时间戳let cur =Date.now()// 时间间隔大于延迟时间则进入if (cur - start >= delay) {//执行事件处理程序func.apply(that, args);//更新初始时间start = cur}}
}
// ---------------------节流2:使用定时器---------------------
//func 是事件处理程序,delay 是事件执行的延迟时间,单位:毫秒
// function throttle(func, delay){
// // 自定义一个定时器
// var timer = null;
// return function(){
// var that = this;
// var args = arguments
// // timer为null表示可以发送请求了,否则还在请求中,不执行事件
// if(!timer){
// timer = setTimeout(function(){
// //执行事件处理程序
// func.call(that, args)
// //事件执行完后把定时器清除掉,下次触发事件的时候再设置
// timer = null;
// }, delay)
// }
// }
// }
button1Dom.addEventListener('click',throttle(function(){console.log("节流:我发送了消息")
},1000))
频繁的点击,降低了发送的频率:
2.5 时间戳与计时器实现的区别
最明显的区别就是在频繁点击的时候,会发现使用时间戳实现的节流会在调用的时候马上执行,而使用计时器实现会在时间段结束时执行
第三章 总结
- 所谓防抖,就是指触发事件后,在 n 秒后函数才会执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间;节流,就是指连续触发事件,但是在 n 秒中只执行一次函数。
- 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。