什么是防抖
我们在前端工作中都会遇到频繁触发的事件处理问题,例如在短时间内多次点击页面上的提交按钮。而解决这类问题的一种常见方法就是使用防抖和节流技术。想象一下,你正在为一个在线搜索功能编写前端代码。用户提交按钮点击五六次,页面频繁地发起请求,这不仅影响了用户体验,还给服务器带来了不必要的压力。而在这种情况下,防抖和节流技术就像是一把锁,能够有效地解决这个问题。
那么防抖究竟是什么?
防抖(Debouncing)是一种常用的前端优化技术,用于处理频繁触发的事件,如浏览器窗口的调整大小、滚动等,以及用户输入的事件,如键盘输入、鼠标移动等。
在防抖技术中,当事件被触发后,不立即执行相应的处理函数,而是等待一段时间(称为防抖延迟期间),如果在这段时间内没有再次触发相同的事件,那么才执行相应的处理函数。如果在延迟期间内又触发了相同的事件,则重新开始计时,直到没有再次触发事件才执行处理函数。那么简单来说防抖就是在规定的时间内没有第二次的操作,才执行逻辑。
手写防抖
那么下面是完整的防抖实现代码:
function debounce(fn, delay){
let timer;
return function(){
//使用arguments关键字
let args = arguments
if(timer) clearTimeout(timer) //取消定时器
timer = setTimeout(() => {
//显式绑定
fn.call(this,...args)
},delay)
}
}
那么我们来分析上述的代码:
防抖函数接受两个参数,fn
是事件触发时真正需要调用的函数,也就是目标函数,delay
是防抖延迟时间。
timer
变量用于设置一个延时执行的定时器,args
变量存储类数组arguments
,因为事件触发的回调函数接受的参数是未知的,所以用类数组表示不定的参数,clearTimeout
方法用来取消定时器,达到重新计时的作用。.call(this)
显式地指定了函数 fn
的执行上下文,即调用防抖函数时的执行上下文,这样可以确保在防抖函数内部执行 fn
函数时,fn
函数中的 this
引用的是正确的对象。
那么防抖代码的主要实现逻辑是什么?我将用一个实例让大家轻松理解!
let btn = document.getElementById('btn')
function send(e){
console.log(this,'提交完成',e);
}
btn.addEventListener('click' , debounce(send,1000))
function debounce(fn, delay){
let timer;
return function(){ //闭包
let args = arguments
if(timer) clearTimeout(timer) //取消定时器
timer = setTimeout(() => {
//显式绑定
fn.call(this,...args)
},delay)
}
}
上述代码实现的是对一个提交按钮进行防抖优化。主要作用是防止用户短时间内点击多次button按钮
分析:
当用户点击button按钮,调用debounce
函数,并传入两个参数,目标函数send和防抖延迟时间1000ms,那么debounce
函数会返回出一个函数,所以当按钮被点击时,实际上会执行 debounce
返回的函数,而不是send
函数,且该函数是一个闭包函数,用于写防抖的逻辑,且闭包保存了fn、delay、timer
。
执行闭包函数的逻辑时,先判断timer
是否为空,很明显如果在防抖延迟时间内,点击了第二次按钮,就会将定时器timer清除,重新设置新的定时器。如果没有,则执行定时器中的回调函数,也就是调用send函数,并传入原始函数的参数。
但是一定要注意两点:
1. send函数的参数是不定的,所以用arguments类数组去保存send函数的参数,然后用解构的形式也就是展开运算符...
来传参。
2. send函数的this指向可能出问题,(如果是小白不了解this的绑定规则的话可以参考这篇文章:(JS)15分钟带你彻底跨越JS的一座大山–this),那么我们通过使用 call
方法将 this
显式绑定到当前(闭包函数)上下文。注意定时器中的回调函数是一个箭头函数,没有this绑定
总结
防抖技术通过延迟执行事件处理函数,有效地控制了事件触发的频率,从而提升了页面性能和用户体验。
那么节流的目标与防抖是一样的,但是设计思路会与防抖略有不同,下节我将继续给大家带来节流的手写,让大家在面试时轻松化解手写的危机。
欢迎大家在评论区留言!
原文链接:https://juejin.cn/post/7342511044290641974 作者:UrGend