防抖和节流
防抖
概念
某个函数,在某段时间内,无论触发了多少次,都只执行最后的一次。
实现原理
利用定时器
/*
* 防抖函数
* @param fn 事件触发的操作
* @param delay 多少毫秒内连续触发事件,不会执行
* @returns {Function}
*/
function debounce(fn, delay) {
let timer = null;
return function () {
let args = arguments;
let that = this;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
fn.apply(that, args);
}, delay);
}
}
使用场景
用户在短时间内,多次点击登陆,发送短信,等请求数据的操作。
文本编辑器一段时间不操作,进行自动保存。
搜索框进行联想,用户不断输入值,只在停顿1s时才发送请求,进行联想。
<!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>防抖和节流</title>
</head>
<body>
<input type="text" id="search" value="">
<script>
/*
* 防抖函数
* @param fn 事件触发的操作
* @param delay 多少毫秒内连续触发事件,不会执行
* @returns {Function}
*/
function debounce(fn, delay) {
let timer = null;
return function () {
let args = arguments;
let that = this;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
fn.apply(that, args);
}, delay);
}
}
var input = document.getElementById('search');
function getData() {
var keyword = input.value;
console.log('输入框关键词是' + keyword + ',正在发送请求,从服务器查找该关键词');
}
// 设置防抖,无操作后的1秒后,才去触发getData事件。
// 如果1秒内连续输入,会不断重置定时器,不会触发getData事件
input.onkeyup = debounce(getData, 1000);
</script>
</body>
</html>
节流
概念
节流与反抖相反,在某段时间内,无论触发了多少次,都只执行最初的一次。
类型
节流有多种不同应用场景的表现形式,一般可以分为三种,分别是首节流,尾节流,兼顾型节流。
首节流
在某段时间内,无论触发了多少次,都只执行最初的一次。
<!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>首节流</title>
</head>
<body>
<button id="btn">提交表单</button>
<script>
/**
* 首节流
* @param fn 事件触发的操作
* @param delay 多少毫秒内连续触发事件,执行第一次
* @returns {Function}
*/
function throttle(fn, delay) {
let last = 0;
return function() {
let now = Date.now();
if (now - last > delay) {
fn.apply(this, arguments);
last = now;
}
}
}
var btn = document.getElementById('btn');
function submitData() {
console.log('向服务器提交表单');
}
// 两秒内连续提交表单,只执行第一次
// 连续点击,每两秒才可以触发一次submitData事件
btn.onclick = throttle(submitData, 2000);
</script>
</body>
</html>
尾节流
尾节流中,第一次是不会直接执行的,而是在一段时间后再执行。
<!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>尾节流</title>
</head>
<body>
<button id="btn">提交表单</button>
<script>
/**
* 尾节流
* @param fn 事件触发的操作
* @param delay 多少毫秒之后触发事件
* @returns {Function}
*/
function throttle(fn, delay) {
let timer = null;
return function () {
let context = this;
let args = arguments;
// 给第一次的点击,设置定时任务
if (!timer) {
timer = setTimeout(function () {
fn.apply(context, args);
timer = null;
}, delay);
}
}
}
var btn = document.getElementById('btn');
function submitData() {
console.log('向服务器提交表单');
}
// submitData事件总是会延迟2秒执行
// 连续点击,每2秒触发一次submitData事件
btn.onclick = throttle(submitData, 2000);
</script>
</body>
</html>
兼顾型节流
兼顾型节流,就能够在第一次,最后一次都执行代码。
经过我的测试,兼顾型节流想要在第一次和最后一次都执行代码,必须在delay的时间内,连点按钮两次。
否则,只触发第一次。
function throttle(fn, delay) {
let last = 0;
let timer = null;
return function () {
let now = Date.now();
let reming = delay - (now - last);
clearTimeout(timer);
if (reming < 0) {
fn.apply(this, arguments);
last = now;
} else {
timer = setTimeout(() => {
fn.apply(this, arguments);
}, reming);
}
}
}