在之前的学习过程中,一直碰到防抖和节流这两个词,但是一直不知道是干什么用的,所以在网上搜索了很多帖子关于防抖和节流的应用场景和代码示例。在这里做一个用法的记录和个人的一些总结。
下面这段代码是有一个div,用户鼠标放在上面会引起里面的数字自增,模拟的情况是用户会多次向后端发起请求,请求速度是非常快的。(本文章代码均引自文末的文章,侵删)
<!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>Document</title>
</head>
<body>
<div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
<script>
let num = 1;
let content = document.getElementById('content');
function count() {
content.innerHTML = num++;
};
content.onmousemove = count;
</script>
</body>
</html>
一、防抖(debounce)
防抖就是触发事件只能在n秒执行一次,如果n秒内再次执行,则会重新计划执行时间。
1. 应用背景
- 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖。比如说有时候网速不好的情况,容易向后端发送多次请求,此时就需要用到防抖。
- 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖。浏览器窗口时间resize触发是相当多的,可以类比平时打印鼠标位置,一瞬间可以打印几十上百条,这里也需要防抖来进行优化。
- 文本编辑器实时保存,当无任何更改操作一秒后进行保存。csdn的发布文章功能就是定时保存草稿,应该用到的就是防抖。
2. 代码示例
防抖分为立即执行防抖和非立即执行防抖。下面分别来看一下实现代码。
立即执行防抖
立即执行大概逻辑就是,第一次触发事件,直接调用函数(立即体现在这里),然后在后面的n秒内再次调用函数都会清空计时器(防抖),必须满足大于n秒,才会重新触发事件,直接调用函数
//立即执行版
function debounce(func,delay){
let timeout;
return function(){
let context = this; //this绑定的是外层的content,不明白的可以看看闭包
let args = arguments;
if (timeout) clearTimeout(timeout); //清除计时器
let callNow = !timeout; //判断是否立即执行
timeout = setTimeout(()=>{
timeout = null;
},delay) //setTimeout 如果在delay时间之内再次触发就会返回一个id
if(callNow) func.apply(context,args); //apply重新绑定作用域和参数
}
}
content.onmousemove = debounce(count,1000);
非立即执行防抖
非立即执行的逻辑是,触发事件后,需要延时等待n秒才会调用函数(非立即),如果这n秒中再次触发事件,就会清空计时器,必须满足大于n秒,才会重新触发事件,调用函数。
//非立即执行版
function debounce(func,delay){
let timeout;
return function(){
let context = this; //this绑定的是外层的content,不明白的可以看看闭包
let args = arguments;
if (timeout) clearTimeout(timeout); //清除计时器
timeout = setTimeout(()=>{
func.apply(context,args); //apply重新绑定作用域和参数
},delay) //setTimeout 如果在delay时间之内再次触发就会返回一个id
}
}
content.onmousemove = debounce(count,1000);
二、 节流(throttle)
节流就是n秒内只能多次触发只能调用一次函数。主要分为定时器版和时间戳版。
1. 应用背景
- 滚轮事件,每隔一秒计算一次位置信息等。
- 浏览器播放事件,每个一秒计算一次进度信息等
- input 框实时搜索并发送请求展示下拉列表,没隔一秒发送一次请求 (也可做防抖)
2. 代码示例
计时器版
function throttle(func ,delay){
let timeout;
return function(){
let context = this;
let args = arguments;
if(!timeout){ //这里相当于开关锁,执行一次后就关锁了,必须等到delay之后,才会开锁
timeout = setTimeout(()=>{
timeout = null;
func.apply(context, delay);
},delay)
}
}
}
content.onmousemove = throttle(count,1000);
时间戳版
function throttle (func , delay){
let prev = 0;
return function(){
let now = Date.now();
let context = this;
let args = arguments;
if(now - prev > delay){ //这里相当于开关锁
func.apply(context, args);
prev = now;
}
}
}
content.onmousemove = throttle(count,1000);
三、总结
- 防抖:防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。代码实现重在清零 clearTimeout
- 节流:控制流量,单位时间内事件只能触发一次。代码实现重在开锁关锁。
参考文章:函数防抖和节流