防抖和节流
什么是防抖和节流
防抖和节流,都是为了防止短时间内高频繁调用同一接口的方法。
防抖
核心:设置延时器,短时间高频率触发只有最后一次触发成功
解释:如果一个事件被频繁执行多次,并且触发的时间间隔过短,则防抖函数可以使得对应的事件处理函数,只执行最后触发的一次。函数防抖可以把多个顺序的调用合并成一次。
无论你如何乱点,也只有你停止点击的最后一次会请求成功。
缺点:响应时间太久。
实现函数防抖
function debounce(fn, wait) {
var timer = null;
// 返回函数对debounce作用域形成闭包
return function () {
var context = this
var args = arguments
if (timer) {
// 事件被触发,清除timer并重新开始计时
clearTimeout(timer);
timer = null;
}
timer = setTimeout(function () {
fn.apply(context, args)
}, wait)
}
}
var fn = function () {
console.log('boom')
}
setInterval(debounce(fn, 500), 1000) // 第一次在1500ms后触发,之后每1000ms触发一次
setInterval(debounce(fn, 2000), 1000) // 不会触发一次(把函数防抖看出技能读条,如果读条没完成就用技能,便会失败而且重新读条)
节流
核心:设置状态锁,短时间高频率触发只有第一次触发成功
解释:如果一个事件被频繁触发多次,节流函数可以按照固定的频率去执行相应的事件处理方法。函数节流保证一个事件一定事件内只能执行一次。
实现函数节流
利用时间戳实现
// fn 是需要执行的函数
// wait 是时间间隔
const throttle = (fn, wait = 50) => {
// 上一次执行 fn 的时间
let previous = 0
// 将 throttle 处理结果当作函数返回
return function(...args) {
// 获取当前时间,转换成时间戳,单位毫秒
let now = +new Date()
// 将当前时间和上一次执行函数的时间进行对比
// 大于等待时间就把 previous 设置为当前时间并执行函数 fn
if (now - previous > wait) {
previous = now
fn.apply(this, args)
}
}
}
// DEMO
// 执行 throttle 函数返回新函数
const betterFn = throttle(() => console.log('fn 函数执行了'), 1000)
// 每 10 毫秒执行一次 betterFn 函数,但是只有时间差大于 1000 时才会执行 fn
setInterval(betterFn, 10)
利用定时器实现
var key = false
btn.onclick = function () {
// if (key == false) {
if (!key) {
console.log('请求');
key = true
setTimeout(function () {
key = false
}, 5000)
}
}
实例(scroll 事件)
css部分
<style>
.wrap {
width: 200px;
height: 330px;
margin: 50px;
margin-top: 200px;
position: relative;
float: left;
}
.header {
width: 100%;
height: 30px;
background-color: #a8d4f4;
text-align: center;
line-height: 30px;
}
.container {
background-color: darkseagreen;
box-sizing: content-box;
width: 200px;
height: 300px;
overflow: scroll;
position: relative;
}
.content {
width: 140px;
height: 800px;
margin: auto;
background-color: cadetblue;
}
</style>
html部分
<body>
<div class="wrap">
<div class="header">滚动事件:普通</div>
<div class="container">
<div class="content"></div>
</div>
</div>
<div class="wrap">
<div class="header">滚动事件:<strong>加了函数防抖</strong></div>
<div class="container">
<div class="content"></div>
</div>
</div>
<div class="wrap">
<div class="header">滚动事件:<strong>加了函数节流</strong></div>
<div class="container">
<div class="content"></div>
</div>
</div>
</body>
js部分
let els = document.getElementsByClassName('container');
let count1 = 0,
count2 = 0,
count3 = 0;
const THRESHOLD = 200;
els[0].addEventListener('scroll', function handle() {
console.log('普通滚动事件!count1=', ++count1);
});
els[1].addEventListener('scroll', debounce(function handle() {
console.log('执行滚动事件!(函数防抖) count2=', ++count2);
}, THRESHOLD));
els[2].addEventListener('scroll', throttle(function handle() {
console.log(Date.now(), ', 执行滚动事件!(函数节流) count3=', ++count3);
}, THRESHOLD));
// 函数防抖
function debounce(fn, delay, scope) {
let timer = null;
let count = 1;
return function () {
let context = scope || this,
args = arguments;
clearTimeout(timer);
console.log(Date.now(), ", 触发第", count++, "次滚动事件!");
timer = setTimeout(function () {
fn.apply(context, args);
console.log(Date.now(), ", 可见只有当高频事件停止,最后一次事件触发的超时调用才能在delay时间后执行!");
}, delay);
}
}
// 函数节流
function throttle(fn, threshold, scope) {
let timer;
let prev = Date.now();
return function () {
let context = scope || this,
args = arguments;
let now = Date.now();
if (now - prev > threshold) {
prev = now;
fn.apply(context, args);
}
}
}
防抖和节流区别
函数节流不管事件触发有多频繁,都会保证在规定时间内一定会真正执行一次事件处理函数,而函数防抖只是在最后一次触发后才会执行。