防抖
防抖函数是前端工具函数必不可少的一个,它用于控制在事件被频繁触发时只执行一次函数。无论是处理用户输入、点击事件还是其他可能导致高频率触发的情况,函数防抖都能有效地减少不必要的执行次数,减少请求次数、优化性能,提升用户体验。
实现
实现函数防抖的核心思想是延迟执行函数,当事件触发时,设置一个定时器,在指定的延迟时间之后再执行函数。如果在这个延迟时间内又有新的事件触发,则重新计时,直到事件停止触发,延迟时间到达后执行函数,知道其原理后,你会手写一个防抖函数吗?
实现一个简单的防抖函数
function myDebounce(handle,wait){
//判断其参数正确性
if(typeof handle !== 'function'){
//这里的handle为托管事件,必须是一个函数,不为函数时应该抛出一个错误
throw new Error('handle must be a Function')
}
if(typeof wait !== 'number'){
//当传入的wait不是一个数字类型或者没有设置wait时应当给一个默认值
wait = 300
}
//定义变量timer保存定时器
let timer = null
//返回一个句柄,当事件触发时,触发这个函数
return function proxy(){
// 如果 timer 不为 null 即在规定时间内已经触发过事件,则清除上一个定时器 这里就是防那个‘抖’
//在规定时间内多次触发这里就会做拦截
if (timer) {
clearTimeout(timer)
timer = null
}
timer = setTimeout(()=>{
handle()
//当过了所规定的时间后 清除其定时器,其目的是为了下一次正常操作时能正常触发事件,
//并把timer 置 null
clearTimeout(timer)
timer = null
},wait)
}
}
这只是一个简易的防抖函数,确实是防抖了,但是其中有很多缺陷:
- 如果我需要传递参数怎么办?this指向?
- 如果我的需求是在频繁的点击,我要求立即触发第一次,不用等待才触发?
解决传递参数和this指向问题
//参数传递和this指向必定只能在防抖函数中返回的那个函数上下功夫 proxy
function proxy(...args){
//保存当前this
let self = this
if(timer) return
timer = setTimeout(()=>{
//通过call方法改变handle的this指向,保证其this为当前触发的对象,采用es6 ...(rest参数)
//获取参数
handle.call(self,args)
clearTimeout(timer)
timer = null
})
}
增加立即执行功能
//如果需要立即去执行这个事件,就是所谓的在频繁的触发下,只触发第一次,并且是立即触发不等待
//就需要额外的配置
function myDebounce(handle,wait,immediate){
//判断其参数正确性
if(typeof handle !== 'function'){
//这里的handle为托管事件,必须是一个函数,不为函数时应该抛出一个错误
throw new Error('handle must be a Function')
}
//这里的参数处理可以采用es6的函数参数的默认值
if(typeof wait !== 'number'){
//当传入的wait不是一个数字类型或者没有设置wait时应当给一个默认值
//如果这里在传参时第二个参数传递的是一个boolean,就把该值赋值给immediate
immediate = wait
wait = 300
}
//如果没有传递正确的immediate或者没传时应给immediate一个默认值
if (typeof immediate !== "boolean") {
immediate = false;
}
//定义变量timer保存定时器
let timer = null
//返回一个句柄,当事件触发时,触发这个函数
return function proxy(...args){
//保存当前this
let self = this
//定义一个变量保存其immediate和timer的状态
//这里这么做的目的是什么呢?
//init就是实现立即触发的关键,当且仅当开启立即执行并且无定时器时立即触发事件
//什么时候无定时器?那必定是第一次执行的时候
let init = immediate && !timer
// 清除上一个定时器
if(timer){
clearTimeout(timer);
timer = null;
}
timer = setTimeout(()=>{
//如果开启了immediate,那必定不会执行定时器里面的handle,触发第一次后,在规定时
//己按内点击都不会触发handle 实现防抖功能
!immediate && handle.call(self,args)
//执行到这里必须要清除其定时器,并把timer置null 以便下一次正常使用
clearTimeout(timer);
timer = null
},wait)
//这里是立即执行
init && handle.call(self,args)
}
}
最后
前端小白,写出来我就是做个记忆巩固,理理思路。有不对的地方还请各位指正一下,谢谢