# 防抖函数
# 面试中我们经常遇到手写一个简单的防抖函数
防抖函数的核心思路如下:
当触发一个函数时,并不会立即执行这个函数,而是会延迟(通过定时器来延迟函数的执行)
- 如果在延迟时间内,有重新触发函数,那么取消上一次的函数执行(取消定时器);
- 如果在延迟时间内,没有重新触发函数,那么这个函数就正常执行(执行传入的函数);
接下来,就是将思路转成代码即可:
定义
debounce
函数要求传入两个参数- 需要处理的函数
fn
; - 延迟时间;
- 需要处理的函数
通过定时器来延迟传入函数
fn
的执行- 如果在此期间有再次触发这个函数,那么
clearTimeout
取消这个定时器; - 如果没有触发,那么在定时器的回调函数中执行即可;
- 如果在此期间有再次触发这个函数,那么
function debounce(fn, delay) {
var timer = null;
return function() {
if (timer) clearTimeout(timer);
timer = setTimeout(function() {
fn();
}, delay);
}
}
注意:当我第一次看到这个代码是还是有点蒙圈,当时我以为var timer=null
每次调用这个防抖函数时都会执行赋值操作,但是实际上并不是这样的,var timer=null
只是对变量timer
进行初始化操作。这里还用到了函数闭包,只要return
后面返回的函数没有执行完毕,则timer
变量会一直保持在栈空间中,不会被销毁。所以在这里timer
变量只会被初始化一次,也就是第一次执行debounce
防抖函数才会执行var timer=null
的初始化赋值操作。
# 下面是另一个版本
//setTimeout()函数来让函数延迟执行
const ipt = document.querySelector('input')//1.获取dom
let timeout = null//2.初始化timeout变量
ipt.addEventListener('input',e=>{//3.被重复调用需要防抖处理的函数
if(timeout){
clearTimeout(timeout)//5.清除定时器
timeout = null
}
timeout = setTimeout(()=> {//4.对timeout变量赋值为一个定时器
search(e.target.value).then(resp => {
//..
},e=>{
//..
})
},500)
})
这个就更好理解一点了,我们来读一下代码,代码中的3、4、5步骤都是会因为防抖被重复调用的。
第一次timeout
初始化为空,if(timout)
里面的内容不执行,对timeout
进行赋值定时器;如果定时器的时间没有过(定时器中的函数没有被执行),此时再次激活监听的事件,timeout
不为空,所以if(timout)
里面的内容会被执行,然后之前的定时器被清除,然后又去设置新的定时器,直到定时器时间过了,执行消息队列消息。
# 总结
函数防抖:将几次操作合并为一此操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
函数节流:使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函数。
区别: 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。