JS函数防抖
触发事件后,在 n 秒内函数只能执行一次,如果触发事件后在 n 秒内又触发了事件,则会清除上一次时间重新计算函数延执行时间。
举栗子
我们都知道声控灯,声控灯是当有声音出现的时候灯亮起几秒钟,几秒钟后自动熄灭。但是如果你在亮起这几秒钟内再发出声音,那么声控灯就会重新计算熄灭时间。假设声控灯亮起间隔为4s,如果你在第2s的时候发出声音,那么声控灯就会将熄灭时间重新改为4s而不是在剩余熄灭的时间叠加4s。我们函数防抖也是这个道理
看栗子
正常
<body>
<input id="btn" type="button" value="快快点我">
</body>
<script>
let btn = document.querySelector('#btn');
btn.addEventListener('click',function(){
console.log(123);
})
</script>
防抖
<body>
<input id="btn" type="button" value="快快点我">
</body>
<script>
let btn = document.querySelector('#btn');
function debounce(fn, delay = 5000) {
let timer = null;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null
}, delay)
}
}
btn.addEventListener('click', debounce(() => {
console.log(123);
}, 1000))
</script>
前端开发过程中,有一些事件,常见的例如,onresize,scroll,mousemove ,mousehover 等,会被频繁触发(短时间内多次触发),不做限制的话,有可能一秒之内执行几十次、几百次,如果在这些函数内部执行了其他函数,尤其是执行了操作 DOM 的函数(浏览器操作 DOM 是很耗费性能的),那不仅会浪费计算机资源,还会降低程序运行速度,甚至造成浏览器卡死、崩溃。这种问题显然是致命的。
学栗子
在分析防抖函数核心代码之前我们得先看看这个
let btn = document.querySelector('#btn');
function clickEvent(){
console.log("我被点击了");
}
btn.addEventListener('click', clickEvent())
你觉得会发生什么事?点击一下按钮就输出一次内容?但事实并非如此,当运行时会直接输出"我被点击了",后续的点击按钮没有任何反应。
为什么? 因为addEventListener第二个参数需要的是一个函数,而你却进行了函数调用
上面的情况相当于:
function TestEvent(fn) {
console.log(typeof fn); // undefined
fn(); // 类型异常,fn不是函数
}
TestEvent(function s() {
console.log(123);
}() )
// Console Print
// 123
// undefined
// TypeError: fn is not a function
我们再看看这个
function clickEvent(){
return function(){
console.log("我被点击了");
}
}
btn.addEventListener('click', clickEvent())
你是否会觉得跟上面发生的事情一样 —— 直接输出内容按钮点击无效。但并不是。因为clickEvent函数内部返回了一个函数。你需要先调用它才能返回函数。
好了,当你搞清楚这些东西后我们掌握防抖函数就是洒洒水了。
第一步:创建一个防抖函数
function debounce(){
}
第二步:接收需要防抖的函数和延迟的时间
function debounce(fn, delay){
}
第三步:防抖核心代码
function debounce(fn, delay) {
let timer = null;
if (timer) { // 如果当前存在定时器直接关闭掉
clearTimeout(timer);
}
timer = setTimeout(() => { // 开启一个定时器
fn()
timer = null;
}, delay)
}
整体测试一下
<body>
<input id="btn" type="button" value="快快点我">
<script>
let btn = document.querySelector('#btn');
function debounce(fn, delay) {
let timer = null;
if (timer) { // 如果当前存在定时器直接关闭掉
clearTimeout(timer);
}
timer = setTimeout(() => { // 开启一个定时器
fn()
timer = null;
}, delay)
}
btn.addEventListener('click', debounce(() => {
console.log(11);
}, 1000))
</script>
</body>
当我们喜出望外的去查看成果时,不出意外出意外了,哈哈,发现出现了我们之前出现的情况 —— 直接输出内容按钮点击无效,只不过这里是延迟了1s输出内容。
为什么会这样呢? 我们之前讲过addEventListener第二个参数需要的是一个函数,而我们这里进行了函数调用。
你听到这里可能就直接跟我急眼了。"你逗我玩是吧,我不用调用我怎么传函数进去"
放下你手中的刀稍安勿躁,还记得我们之前说过的函数内部返回一个函数,我们修改一下代码:
function debounce(fn, delay) {
let timer = null;
return function () {
if (timer) { // 如果当前存在定时器直接关闭掉
clearTimeout(timer);
}
timer = setTimeout(() => { // 开启一个定时器
fn()
timer = null;
}, delay)
}
}
// 调用
debounce(()=>{
console.log(123)
},1000)()
我们去掉后面的括号让addEventListener帮我们调用,整体代码:
<body>
<input id="btn" type="button" value="快快点我">
<script>
let btn = document.querySelector('#btn');
function debounce(fn, delay) {
let timer = null;
return function () {
if (timer) { // 如果当前存在定时器直接关闭掉
clearTimeout(timer);
}
timer = setTimeout(() => { // 开启一个定时器
fn()
timer = null;
}, delay)
}
}
btn.addEventListener('click', debounce(() => {
console.log(11);
}, 1000))
</script>
</body>