防抖
在我们处理一些响应的时候,我们总是会发现总有些人得"抖上两下"
,比如提交的时候要点个三四遍。又比如像我们正常使用的时候,遇到网络问题点击提交后没有反应,我们也会,诶?在点两下,可是这样就会发送很多无用的信息,我们要怎么解决呢?我们一般称这个为防抖。
JS防抖是一种比较常用的前端技术,我们经常会在面试中遇到面试官可能就会问你能不能手写一个防抖,因为它涉及了闭包
,this绑定规则
,箭头函数
,argument
等细节,它可以延迟函数的执行,并且在一定时间内只允许函数执行一次。这种技术在处理一些高频事件时非常有用,如窗口大小改变事件、滚动事件等,可以有效减少事件的频繁触发,提高页面的性能。
原理
那么JS防抖的实现原理是什么呢?我们可以通过以下几步来详细介绍:
-
首先,我们需要了解什么是防抖。防抖是指在一定时间内,只执行一次函数。如果在这段时间内再次触发了该函数,那么就重新计时。这样的话我们就可以避免函数的频繁执行,每在一个固定的时间内出现了下一次触发我们就清除上一次,执行下一次并且重新计时。
-
接下来,我们需要实现一个
防抖函数
。这个函数可以接收两个参数:要执行的函数和防抖时间。在函数内部,我们需要使用一个定时器来延迟函数的执行。如果在延迟时间内再次触发了该函数,那么就清除之前的定时器,并重新计时。 那么根据这个我们就有:
js
function debounce(fn,delay){
let timer
return function(){
if(timer) clearTimeout(timer)
timer = setTimeout(function (){
fn()
},delay)
}
}
我们清除上一次的操作就可以通过闭包来实现,也就是在return
函数内调用debounce
的timer
保存上一次的定时器,这样就会在debounce执行上下文
出栈的时候产生闭包保存下来,让我们之后能够进行更改。(如果是小白不知道的话可以看一下这篇文章: 原来闭包可以这么简单
但是还没有结束,这只能称得上丐版的防抖,我们应该再给它穿些什么衣服充实一下它呢?
- 在实现防抖函数的过程中,我们还需要注意一些细节。例如,我们的函数可能会传递参数,而我们在使用之前并不知道会有多少个参数传入,所以我们要如何传递未知数量的参数呢?还有就是万一调用了
this指针
,我们要怎么确保指针指向我们所需要的呢?让我们一起来看看:
手写完整实现
下面是一个基本的JS防抖函数的实现代码:
js
function debounce(fn,delay){
let timer
return function(){
// arguments
let args = arguments
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.call(this,...args)中的this才会是return 的 function的this指针。
},delay)
}
}
- 首先我们为了传递未知个数的参数,我们可以使用
argument
,argument其实是每一个函数中自带的一个类数组,让我们打印一下看看:
他其实是一个包含了我们传入的参数的类数组,所以我们可以通过解构的方式也就是展开运算符'...'(如果是小白的话可以参考这篇文章: ES6新特性全面汇总39条你知道多少?(二))来重新将参数拆出来传递到我们的fn中。
-
然后我们就需要绑定指针,而我们fn的指针原本应该是return的function上的this指针,那么我们可以通过显示绑定(如果是小白不了解this的绑定规则的话可以参考这篇文章: 函数在哪里调用,this就指向哪里,真的对吗?)从而将调用的fn函数中的this转为指向
return 的 function
的this指针
-
但你会发现
箭头函数
有什么特别的呢,为什么改为使用箭头函数
?如果你看了那篇this绑定规则
的文章后,你就会知道箭头函数
没有this指针
,所以fn.call(this,...args)
中的this
才会是return 的 function
的this指针
。
这样,我们就实现了一个基本的JS防抖函数。在实际应用中,我们可以根据具体的场景来设置防抖时间,以达到最佳的性能和用户体验。
我们来一个示例吧:
js
let btn = document.getElementById('btn')
function send(e){
console.log('已提交',e);
}
btn.addEventListener('click',debounce(send,1000))
function debounce(fn,delay){
let timer
return function(){
// arguments
let args = arguments
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.call(this,...args)
},delay)
}
}
通过这样一个防抖函数,我们就可以在用户疯狂点击的时候不调用send函数,直到用户点击了1000ms后没有下一次点击才进行调用。
总结
总之,JS防抖是一种比较有用的技术,我们可以使用防抖的方式在高频事件的处理中提高页面性能和用户体验。熟练掌握其实现原理和细节,对于前端开发人员来说是非常重要的,而且面试时很有可能会碰到手写一个防抖函数。
节流函数虽然有些相似但又有一些不同的细节,我们就放到下一部分来介绍吧~
那么文章到这里就结束啦~
如果你想了解更多这类文章,点赞关注作者更新更多后续~