防抖 (debounce)
函数的防抖:对于频繁触发某个操作,我们只识别一次(只触发执行一次函数)
inde.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scal=1.0">
<title>防抖</title>
<style>
* {
margin: 0;
padding: 0;
}
html,body {
height: 150%;
background: -webkit-linear-gradient(top left, lightblue, lightgreen, orange);
}
#submit {
margin: 50px;
}
</style>
</head>
<body>
<button id="submit">提交按钮</button>
<script src="debounce.js"></script>
</body>
</html>
debounce.js
function handle() {
console.log(submit)
// 设置定时器
setTimeout(()=> {
console.log("OK");
}, 1000);
}
submit.onclick = handle;
疯狂点击按钮的时候,执行很多次handle,对于这种频繁操作,希望只触发一次操作。
/**
*
* @param {*} func[function] 要执行的方法
* @param {*} wait[number] 等待执行的时间(用户自己规定多长时间内不算频繁的操作)
* @param {*} immediate 疯狂点击的时候最终只识别一次,识别第一次还是最后一次,immediate为true,立即执行(第一次)
* @return 可以被调用执行的函数
*/
// 在当前点击完成后,在wait时间内,是否还会触发第二次,如果没有触发第二次,属于非频繁操作,直接执行想要执行的函数func;如果触发了第二次,则clear之前的时间,从当前这次再开始等待...
function debounce(func, wait = 300, immediate = false) {
let timer = null;
return function (...args) {
// let now = immediate && timer === null;
let now = immediate && !timer;
// 每次点击都把之前设置的定时器清除
clearTimeout(timer);
timer = setTimeout(() => {
// 手动让其回归到初始状态(下一轮开始还是从timer为null开始),没加这句,只会执行一次,就没有了
timer = null;
immediate ? null : func.call(this, ...args);
}, wait);
// 如果是立即执行
now ? func.call(this, ...args) : null;
};
}
// function handle() {
// console.log(submit)
// // 设置定时器
// setTimeout(()=> {
// console.log("OK");
// }, 1000);
// }
function handle() {
console.log("OK");
}
// submit.onclick = handle; // 点击一次触发执行一次handle,频繁触发,频繁执行handle
// 原理:在匿名函数中,控制handle只执行一次
submit.onclick = debounce(handle, 3000, true);
节流 (throttle)
函数节流: 在一段频繁操作中,可以出发多次(不像防抖,只一次),但是触发的频率自己指定。
function handle() {
// 设置定时器
console.log('OK');
}
window.onscroll = handle;
现象:随着页面的滚动,一直打印字符串’ok’。
每一次滚动过程中,浏览器有最快反应时间(5~6ms, ie:13~17ms),只要反应过来就会触发一次函数(此时
利用throttle控制频率为300ms触发一次。
/**
* 函数节流:在一段频繁操作中,可以触发多次,但是触发的频率由自己指定
* @param {*} func[function]:最后要触发执行的函数
* @param {*} wait[number]:触发的频率
*/
function throttle(func, wait = 300) {
let timer = null,
previous = 0; // 记录上一次操作的时间
// return出匿名函数,相当于每隔5~6ms触发一次匿名函数,我们可以在匿名函数中控制handle的频率
return function () {
let now = new Date(),
remaining = wait - (now - previous); // 记录还差多长时间达到我们一次触发的频率
if (remaining <= 0) {
window.clearTimeout(timer); // 快满足300ms时,remaining就会小于0,会执行func,这时候定时器里的待执行func不能再执行,需要clear
timer = null;
previous = now;
// 俩次操作的间隔时间已经超过wait了
func, call(this, ...params);
} else if (!timer) {
// timer === null timer不存在,还没设置,开始启动定时器
// 俩次操作的间隔时间不符合触发的频率
timer = setTimeout(() => {
timer = null;
previous = new Data();
func.call(this, ...params);
}, remaining);
}
};
}
window.onscroll = throttle(handle);
=============================================================================
防抖 (debounce)
防抖,顾名思义,防止抖动,以免把一次事件误认为多次,敲键盘就是一个每天都会接触到的防抖操作。
想要了解一个概念,必先了解概念所应用的场景。在 JS 这个世界中,有哪些防抖的场景呢?
- 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
- 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
- 文本编辑器实时保存,当无任何更改操作一秒后进行保存
代码如下,可以看出来防抖重在清零 clearTimeout(timer)
function debounce (f, wait) {
let timer
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => {
f(...args)
}, wait)
}
}
节流 (throttle)
节流,顾名思义,控制水的流量。控制事件发生的频率,如控制为1s发生一次,甚至1分钟发生一次。与服务端(server)及网关(gateway)控制的限流 (Rate Limit) 类似。
scroll
事件,每隔一秒计算一次位置信息等- 浏览器播放事件,每个一秒计算一次进度信息等
- input 框实时搜索并发送请求展示下拉列表,没隔一秒发送一次请求 (也可做防抖)
代码如下,可以看出来节流重在开关锁 timer=null
function throttle (f, wait) {
let timer
return (...args) => {
if (timer) { return }
timer = setTimeout(() => {
f(...args)
timer = null
}, wait)
}
}
总结 (简要答案)
- 防抖:防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。代码实现重在清零
clearTimeout
- 节流:控制流量,单位时间内事件只能触发一次,如果服务器端的限流即 Rate Limit。代码实现重在开锁关锁
timer=timeout; timer=null