debounce/throttle
定义
- 防抖(debounce):指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间
- 节流(throttle):指连续触发事件但是在 n 秒中只执行一次函数。两种方式可以实现,分别是时间戳版和定时器版
实现
防抖(debounce)
有这样一个需求,用户向输入框输入内容,当输入框内容改变就向服务器发送请求,我们可以这样来实现:
<h1>防抖</h1>
<input id="input">
// 获取input元素
const input = document.getElementById("input")
// 绑定输入事件
input.addEventListener("input", function (e) {
console.log("发送请求"+ e.target.value);
})
如果不加防抖结果就是这样的:
可以发现,用户每输入一个字符都会发送一次请求,这样会对服务器造成一定压力,我们想让用户输入完之后,过几秒再去发送请求,这时候就可以用防抖函数来解决:
<!DOCTYPE html>
<html lang="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>debounce</title>
</head>
<body>
<h1>防抖</h1>
<input id="input">
<script>
// 防抖函数,接收两个参数,一个是要执行的函数,另一个是延迟时间
function debounce(fn, delay) {
// 定义一个空的变量
let timer = null;
// 返回一个函数
return function(){
// 判断timer是否为空
if (timer) {
// 如果不为空,则清除定时器timer
clearTimeout(timer);
}
// 给timer赋值一个定时器
timer = setTimeout(() => {
// 执行fn函数,改变函数的this指向,并传递参数
fn.apply(this, arguments)
}, delay);
}
}
// 获取input元素
const input = document.getElementById("input")
// 绑定输入事件
input.addEventListener("input", debounce(function (e) {
console.log("发送请求"+ e.target.value);
}, 500))
</script>
</body>
</html>
加上防抖函数之后,就变成了这样:
节流(throttle)
我们又有另一个需求,当我们对一个盒子进行拖拽时,每拖拽到一个地方就把位置发送给服务器,我们可以这样实现:
<h1>节流</h1>
<div id="div" draggable="true"></div>
#div {
height: 100px;
width: 100px;
background-color: orange;
}
// 获取input元素
const divDom = document.getElementById("div");
// 绑定拖拽事件
divDom.addEventListener("drag", function (e) {
console.log("拖拽到" + e.offsetX + "," + e.offsetY);
})
不加节流函数结果是这样的:
可以看到请求发送的是非常频繁的,这样对服务器的压力是很大的,因此我们需要加一个节流函数,下面是两种版本的节流函数:
<!DOCTYPE html>
<html lang="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>throttle</title>
<style>
#div {
height: 100px;
width: 100px;
background-color: orange;
}
</style>
</head>
<body>
<h1>节流</h1>
<div id="div" draggable="true"></div>
<script>
// 定时器版节流函数,接收一个函数和一个延迟时间
function throttle(fn, delay) {
// 定义一个初始状态,为false
let flag = false;
// 返回一个函数
return function () {
// 如果flag为true,则直接返回,不再往下执行
if(flag) return;
// 修改flag为true
flag = true
// 设置定时器
setTimeout(() => {
// 执行fn函数,改变函数的this指向,并传递参数
fn.apply(this, arguments);
// 修改flag为false
flag = false
}, delay)
}
}
// // 时间戳版节流函数,接收一个函数和一个延迟时间
// function throttle(fn, delay) {
// // 初始时间为0
// let time = 0;
// return function () {
// // 获取当前时间戳
// let now = new Date().valueOf();
// // 判断当前时间减去上一时间是否大于延迟时间间隔
// if (now - time >= delay) {
// // 执行fn函数,改变函数的this指向,并传递参数
// fn.apply(this, arguments);
// // 让time等于当前时间
// time = now;
// }
// }
// }
// 获取input元素
const divDom = document.getElementById("div");
// 绑定拖拽事件
divDom.addEventListener("drag", throttle(function (e) {
console.log("拖拽到" + e.offsetX + "," + e.offsetY);
}, 200))
</script>
</body>
</html>
加上节流函数之后,结果就好多了: