概念
1. 防抖(Debounce)
函数防抖,就是指触发事件后,在 n 秒内函数只能执行一次,如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间。
通俗的讲: 一段时间内重复触发,只执行开始一次和结尾一次,或者只执行结尾那次
使用场景:浏览器页面onresize,scroll,mousemove ,mousehover 等,会被频繁触发(短时间内多次触发)的时候就需要用到防抖,或者某些点击事件,防止多次点击造成频繁请求后台。
2. 节流(Throttle)
函数节流,就是指触发事件后,在n秒内函数只能执行一次,如果触发事件后在n秒内又触发了事件,则会再次执行该事件。顾名思义,就是像水管流水一样,有频率的执行。
通俗的讲: 一段时间内重复触发,按一定频率(3s、5s)执行,可配置一开始就执行一次
使用场景:浏览器页面onresize,scroll,mousemove ,mousehover 等,会被频繁触发(短时间内多次触发)的时候就需要用到节流,例如浏览器发生滚动事件时,每3秒或5秒执行一次事件。
使用
1. 手写防抖函数
编写前思考:
- debounce是一个函数,并且需要传入一个函数handle
- 传入函数后,我们要传入一个防抖时间wait
- 需要判断是否立即执行immediate
- 返回一个一个函数 fn
编写
function debounce(handle,wait,immediate){
//首先进行参数判断
if(typeof handle !== 'function') throw new Error('handle must be function')
//默认wait为1s
if(typeof handle === 'undefinde') wait = 1000
//如果只传入 handle 和 immediate
if(typeof wait === 'boolean'){
immediate = wait
wait = 1000
}
//immediate默认为flase
if(typeof immediate !== 'boolean') immediate = false
//所谓的防抖效果,我们想要实现的就是一个人为可以管理handle的执行次数
let timer = null
return function proxy(...args){
let self = this,
init = immediate && !timer
clearTimeout(timer)
timer = setTimeout(()=>{
timer = null
!immediate ? handle.aplly(self,args) : null
},
wait)
//如果立即执行
init ? handle.apply(self,args) : null
}
}
HTML中使用
<button type="button" id="btn1">防抖</button>
//点击函数
function clickHandle(){
console.log('防抖演示')
}
document.getElementById('btn1').addEventListener('click',debounce(clickHandle,3000,true))
VUE中使用
<button type="button" @click="clickHandle">防抖</button>
//method方法中
methods:{
clickHandle: debounce(function(){
console.log('防抖演示')
},3000,true)
}
手写节流函数
编写前思考:
- debounce是一个函数,并且需要传入一个函数handle
- 传入函数后,我们要传入一个节流时间wait
- 需要判断是否立即执行immediate
- 返回一个一个函数 fn
编写
//通过时间差实现
function throttle(handle,wait,immediate){
if (typeof handle !== 'function') throw new Error('handle must be an function')
if (typeof wait === 'undefined') wait = 1000
//如果只传入 handle 和 immediate
if(typeof wait === 'boolean'){
immediate = wait
wait = 1000
}
//immediate默认为flase
if(typeof immediate !== 'boolean') immediate = false
//定义变量记录上一次执行的时间
let previous = 0
let timer = null
return function proxy(...args){
//获取当前时间
let now = new Date()
let self = this
//如果不立即执行
if(!immediate) previous = now
// 计算间隔时间
let interval = wait - (now - previous)
if(interval <= 0){
// 此刻就说明是一个非高频次操作,可以执行操作
clearTimeout(timer)
timer = null
handle.apply(self,args)
previous = new Date()
}else if(!timer){
//当我们发现系统中有一个定时器了,就意味着我们不需要再开定时器
//此刻就说明这次的操作发生了在我们定义的频次时间范围内,那就不应该执行handle
// 这个时候我们就可以自定义一个定时器,让handle在interval之后去执行
timer = setTimeout(()=>{
clearTimeout(timer) //清除定时器
timer = null
handle.apply(self,args)
previous = new Date()
},interval)
}
}
}
//通过定时器实现(简洁版)
function throttle(fn, wait = 1000, immediate = false) {
let flag = true
let timer = null
return function(...args) {
if (flag) {
//是否立即执行
immediate && fn.apply(this, args)
//关闭通道,等待定时器执行完后再开启
flag = false
timer = setTimeout(() => {
!isImmediate && fn.apply(this, args)
flag = true
}, wait)
}
}
}
使用
节流的使用和防抖一样,参考上面防抖得到使用
总结
防抖和节流在日常开发中经常会遇到,建议将其封装到项目的功能函数中。特别是点击提交按钮时,为了防止用户误点提交多次,可以在内部加一个1s左右的防抖函数。