什么是防抖函数
在某一个时间段内,一个函数频繁快速的调用,只执行最后一次的调用。
防抖函数实际应用场景
我们在执行一个数据搜索功能时,通过监听input框的值,值变化触发搜索,
如果我们在输入框输入"zhangsan",因为值变化了八次,会触发八次搜索,但是,我们只需要在输入完成后触发一次搜索就行。
防抖函数的基本思路
为了不让函数频繁的触发,就需要给搜索方法添加一个定时(也可以说延时),当我们输入"z"时给搜索方法添加延时1000ms(1s),函数处于等待状态1秒后执行,紧接着1秒之内我们输入了"h",清除原有的定时重新计算时间,直到输入最后一个字符,再1秒执行搜索。
实现防抖函数之前先介绍一下定时器
什么是定时器
全局的 setTimeout()
方法设置一个定时器,一旦定时器到期,就会执行一个函数或指定的代码片段。
定时器的使用
setTimeout(() => {
console.log("延迟了 1 秒");
}, 1000);
和异步函数的配合
setTimeout()
是异步函数,意味着计时器函数将不会暂停函数栈中其他函数的执行。也就是说,你不能使用 setTimeout()
来在函数栈中下一个函数执行前执行“暂停”操作。
查看以下示例:
setTimeout(() => {
console.log("这是第一条消息");
}, 5000);
setTimeout(() => {
console.log("这是第二条消息");
}, 3000);
setTimeout(() => {
console.log("这是第三条消息");
}, 1000);
// 输出:
// 这是第三条消息
// 这是第二条消息
// 这是第一条消息
请注意,第一个函数在调用第二个函数之前没有建立一个 5 秒钟的“暂停”。相反,第一个函数被调用,但等待 5 秒后执行。当第一个函数等待执行时,第二个函数被调用,在第二个函数执行之前,对其进行了 3 秒的等待。由于第一个和第二个函数的定时器都没有完成,第三个函数被调用并首先完成其执行。第二个函数紧随其后。在第一个函数的定时器最终完成后,最后执行该函数。
返回值
返回值 timeoutID
是一个正整数,表示由 setTimeout()
调用创建的定时器的编号。这个值可以传递给 clearTimeout() 来取消该定时器。
输出
取消定时
正确的取消定时
clearTimeout(timer)
let timer = setTimeout(() => {
console.log("1s后执行");
console.log("timer=",timer);
},1000)
clearTimeout(timer)
不正确的取消定时
不正确的取消定时(一)
let timer = setTimeout(() => {
console.log("1s后执行");
console.log("timer=",timer);
},1000)
timer = null
不正确的取消定时(二)
let timer = setTimeout(() => {
timer = null
console.log("1s后执行");
console.log("timer=",timer);
},1000)
不正确取消定时(三)
let timer = setTimeout(() => {
clearTimeout(timer)
console.log("1s后执行");
console.log("timer=",timer);
},1000)
为什么timer=null不能取消定时
setTimeout函数的返回值是一串数字,取消定时的原理是根据这一串数字找到对应的定时任务然后再清楚定时。
timer=null只是可以让浏览期垃圾回收这个变量而已,并不会调用清除(clearTimeOut)方法去清楚定时
防抖函数的实现
<!DOCTYPE html>
<html lang="CH-EN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>防抖函数</title>
</head>
<body>
<div class="content">
<input name="value" style="width: 200px" />
<button onclick="btnclick()">btnclick</button>
</div>
</body>
</html>
<script>
// 使用防抖函数多次点击仅触发一次
btnclick = debounce(function () {
console.log("点击了按钮");
}, 1000);
const input = document.querySelector("input");
// input 输入变化的监听
input.addEventListener(
"input",
debounce(function (e) {
// input值 插入页面中
insertEle(e.target.value);
insertEle("触发搜索");
}, 1000)
);
// 防抖函数
function debounce(fn, delay) {
// 定义防抖Id (timer)
let timer = null;
return (...args) => {
if (timer) {
clearTimeout(timer);
}
// 储存定时(setTimeOut)的返回值 ,清除方法根据timer去清楚定时;
timer = setTimeout(() => {
// timer=null 便于方法执行完成后垃圾回收
timer = null;
fn.apply(this, args);
}, 1000);
};
}
// input 框中插入新元素
function insertEle(str) {
const content = document.querySelector(".content");
let ele = document.createElement("div");
ele.innerText = str;
content.insertBefore(ele, input);
}
</script>
<style>
* {
margin: 0;
padding: 0;
}
body {
display: flex;
align-items: center;
justify-content: center;
}
.content {
height: 300px;
width: 500px;
border: solid #ddd 1px;
text-align: center;
padding: 20px;
}
.content input {
height: 24px;
line-height: 24px;
font-size: 18px;
border: 1px solid #dcdfe6;
}
.content input:focus {
outline: none;
border-color: #409eff;
}
.content input:focus-visible {
outline-offset: 0px;
}
</style>
效果
输入框连续输入数据
多次点击按钮