多年前,你是否经历过这种骗局:当电脑进度条龟速前进甚至卡死的时候,有没有人教你只要你狂按enter,电脑响应就会变快。
多多少少受这个谣言的影响,部分人现在点个确认提交1s内能狂点10下,如果所有用户都这样,1s对服务器发起十几甚至几十次请求,不仅服务器得崩,没准一气之下,把电脑也砸了(手动狗头)。
因此,有了防抖和节流这两种解决办法,本文先介绍防抖,典型应用场景有以下几种:
- scroll事件滚动触发;
- 搜索框输入查询
- 表单验证
- 按钮提交
- 浏览器的窗口缩放,resize事件
于是我跟着可爱的马勾勾老师,一起走了一遍防抖代码,看其内部实现机制,源码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#div1{
display: flex;
justify-content: center;
align-items: center;
height: 200px;
background-color: pink;
color: grey;
}
#div2{
display: flex;
justify-content: center;
align-items: center;
height: 200px;
background-color: lightcoral;
color: #ffffff;
}
</style>
</head>
<body>
<div id="div1"></div>
<div id="div2"></div>
<button>关闭防抖</button>
<script>
function debounce(fn,wait,immediate){ //参数意义:想要限制次数的事件,延时时长,等待/立即执行
let timer, result; //timer定时器,result接受fn可能存在的返回值
// console.log(this); //优化前是window
let debounced = function(){
// console.log(this); //优化前是odiv
// console.log(arguments);
let _ = this; // 使函数调用时的this指向事件发生对象
let args = arguments; // 使函数调用时的event指向事件发生对象
clearTimeout(timer); // wait时间内再次触发时间,从新开始计时
if(immediate){
// 鼠标移动立即执行
// 初次timer为空,fn能立即执行,否则会等待wait时间才能再次立即触发
let now = !timer;
timer = setTimeout(()=>{
timer = null;
},wait);
if(now) result = fn.apply(_, args);
}
else{
// 第一次不会立即执行,等待wait时间后执行第一次
timer = setTimeout(function () {
result = fn.apply(_, args);
}, wait);
}
return result;
};
// 取消防抖事件
debounced.cancel = function(){
clearTimeout(timer); //清空定时器
timer = null; //释放定时器(存在闭包)
};
return debounced;
}
let count = 0;
let num = 0;
let odiv1 = document.querySelector('#div1');
let odiv2 = document.querySelector('#div2');
let obtn = document.querySelector('button');
function doSomething(ev){
// console.log(ev); //优化前是undefined
odiv1.innerHTML = count++;
// console.log(this); //odiv
}
setInterval(function () {
odiv2.innerHTML = num++;
},1000);
let dosome = debounce(doSomething, 5000);
obtn.onclick = function () {
dosome.cancel();
};
odiv1.onmousemove = dosome;
</script>
</body>
</html>