理解js防抖和节流只需要这一篇文章(详细到每一行代码的实现和使用场景)
一、防抖
- 防抖:在事件被触发n秒后再执行回调函数,如果在这n秒内又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。
代码实现
我们分析防抖的概念,首先声明防抖函数debounce;
function debounce(){}
我们有如下一个常见需求,获取用户在input框中的值。
<input type="test" oninput="getInputValue(event)">
<script>
function getInputValue (event){
console.log(event.target.value)
}
</script>
getInputValue()这个函数就能实时的拿到用户在input中输入的值,但是有一个弊端,只要用户输入了值就会马上触发这个方法,同时会多次回调。比如用户输入1234,那么这个方法就会触发四次,打印四次console,对于console来说确实不怎么消耗性能,但是我们如果要把这个数据用作后台的ajax请求交互,比如常见的搜索功能,那么就非常的消耗性能了,上述例子来讲,打印四次console,就相当于四次ajax请求,这是非常没有必要的。所以此时就是我们防抖就有大作用了。
有了上述的使用场景,我们继续来讲设计思路,还是以上诉为例,我们可以这么去做,我们在获取input中的值的时候以1s为基准,如果用户在1s内输入了1234,那么我们就在1s后再去读取输入的值,然后把数据给后端。如果用户输入1234用了两秒,那么我们可以先判断1s内用户是否还在输入,发现用户还在输入我们再等1s,然后读取一次。然后再把数据给后台,依次类推。当然我们还可以以2s,3s为基准,这都是可以的。
结合上面的思路我们来实现防抖。
- 一个时间基准:wait;一种操作:fn;(上述的ajax请求后台阶交互)
function debounce(fn,wait){}
2.上述例子中1秒后读取值,时间基准读取值,我门可以使用js中的setTimeout()延时函数
function debounce(fn,wait){
setTimeout(()=>{
fn()
},wait)
}
3.如果用户在基准时间内还在进行操作,那么我门要延长获取数据的时间。在实现这个这个功能时我们需要具备闭包的知识。同时要知道setTimeout()是有返回值的,它会返回一个序列号。我们可以使用clearTimeout(number)马上清除setTimeout()的延时,number就是setTimeout()的序号返回值。
3.1 我们再细一点。首先实现我们实现延时的清除。
function debounce(fn,wait){
let timer;
if(timer){ //这里判断是否有延时序列号,有才清除。
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn()
},wait) //这里上面有讲,setTimeout有返回值,是序列好,clearTimeout传入序列号,可清除响应的延时。
}
3.2 好了,到这里我们只差最后一步啦。现在我们把之前的需求实现一下,看看有什么问题。
<input type="test" oninput="getInputValue(event)">
<script>
function debounce(fn,wait){
let timer;
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn()
},wait)
}
const ajax = debounce((val)=>{
console.log('使用val做后台交互')
},500)
function getInputValue (event){
ajax(event.target.value)
}
</script>
走一遍上述的代码会发现,我们毎调用一次ajax函数,就是调用debounce,然后每次都会生成一个timer;timer始终是undefined;始终不会走clearTimeout;没有达到我们要求,
解决办法,我们可以使用必包啦。我们让debounce有一个返回值,返回一个函数。timer(闭包会继承外部函数的AO)类似一个标记。然后我们再看以下代码,最后我们的调用的参数别搞忘了;
<input type="test" oninput="getInputValue(event)">
<script>
function debounce(fn,wait){
let timer;
return (...args) => {
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn(...args)
},wait)
}
}
const ajax = debounce((val)=>{
console.log('使用val做后台交互')
},500)
function getInputValue (event){
ajax(event.target.value)
}
</script>
最后得到我们的防抖函数
function debounce (fn,wait) {
let timer;
return (...args) => {
if(timer){
clearTimeout(timer)
}
timer = setTimeout(() => {
fn(...args)
},wait)
}
}
二、节流
- 节流:规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。
代码实现;
上面我们详细的讲解了防抖,节流也是一样的逻辑,区别在与,防抖会清除计数器,节流多一个状态。我们直接附上代码
function throttle (fn,wait) {
let isWaiting = false
return (...args) => { //这里还是用闭包,上面有原因
if (isWaiting){
return
}
fn(...args);
isWaiting = true;
setTimeout(() => {
isWaiting = false
},wait)
}
}
从上面的代码我们其实就发现,节流就是加一个等待的延时器,只有当延时器执行,将isWaiting变成false后才能执行fn;
转载自:https://juejin.cn/post/7132874962787893261