防抖和节流分析
分类:javascript
防抖
概念
防抖就是特定时间内,防止重复操作执行多次事件处理函数
即在一段连续的时间内,多次的同一操作(例如点击)对应的事件处理函数只触发一次
应用场景
- 登录、发请求等按钮避免用户点击太快,以致于发送了多次请求,需要防抖 --- 立即执行
- 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖 --- 非立即执行
- 输入框内容校验时,等用户输入完成后再校验,需要用到防抖 --- 非立即执行
实现方式
从上面两个应用场景,根据执行函数是否在操作后立即执行
防抖细分为以下两个类型,立即执行(场景1)和非立即执行(场景2/3)
接下来我们从点击按钮执行+1这个简单案例来实现两种防抖效果
首先看下不加防抖的代码和效果
<button id="btn1">不加防抖 +1</button>
<button id="btn2">防抖,函数非立即执行 +1</button>
<button id="btn3">防抖,函数立即执行 +1</button>
<div id="app">
0
</div>
let count = 0;
function add() {
const app = document.querySelector('#app');
count++;
app.innerHTML = `<span>${count}</span>`;
}
document.querySelector('#btn1').addEventListener('click', add);
非立即执行
- 将事件处理函数放在定时器
setTimeout
内作为异步事件执行 - 当防抖函数被再次调用,判断上一个定时器是否存在,如果存在则清除上一个定时器
- 清除定时器达到只执行一次事件处理函数的目的
按照以上思路代码如下:
function debounce(fn, times, ...args) {
let timer = null;
return function () {
const content = this;
timer && clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(content, args);
}, times);
}
}
const addDebounce = debounce(add, 500);
document.querySelector('#btn2').addEventListener('click', addDebounce);
立即执行
立即执行和非立即执行的区别在于事件处理函数要先执行,后续的重复操作不会再次触发事件处理函数,所以事件函数不能放在setTimeout
内
- 事件处理函数放在
setTimeout
外 - 通过判断上一个定时器是否存在来执行事件处理函数,如果上一个定时器不存在则执行事件处理函数,存在则清除定时器
- 定时器
setTimeout
内部清除自身
按照以上思路代码如下:
function debounceImmediately(fn, times, ...args) {
let timer = null;
return function () {
timer && clearTimeout(timer);
let isDone = !timer;
timer = setTimeout(() => {
timer = null
}, times);
isDone && fn.apply(this,args)
}
}
const addDebounceImmediately = debounceImmediately(add, 500)
document.querySelector('#btn3').addEventListener('click', addDebounceImmediately);
节流
概念
节流就是减少事件处理的频次
即在一高频触发的场景下(例如scroll事件),截取一段连续的时间,该时间内事件只触发一次,达到降低事件处理函数执行的频次
应用场景
- 图片懒加载,滚动条下滑以一定的频率被触发图片加载
- mousemove事件,每隔一段时间变动坐标
实现方式
接下来我们就以mousemove事件,每次移动+1,作为一个简单案例来说明节流
先看下不加节流时的效果
let count = 0;
function add() {
const app = document.querySelector('#app');
count++;
app.innerHTML = `<span>${count}</span>`;
}
document.addEventListener('mousemove',add);
状态锁
- 节流函数初始状态设置为true,当状态为true允许定时器执行
- 一旦开启定时器,状态设置为false,不允许下一次的节流函数执行
- 事件处理函数放在
setTimeout
内,当定时器时间到达,事件处理函数执行,状态设置为true
按照以上思路代码如下:
function throttle(fn, times, ...args) {
let isDone = true;
return function () {
if (isDone) {
const context = this;
setTimeout(() => {
fn.call(context, ...args);
isDone = true;
}, times);
isDone = false;
}
}
}
const addThrottle = throttle(add,500);
document.addEventListener('mousemove',addThrottle);
时间戳
- 定义初始时间 startTime 为0,节流函数执行获得当前时间
nowTime=Date.now()
- 当
nowTime - startTime > times
执行事件处理函数,重置startTime=Date.now()
为当前时间
按照以上思路代码如下:
function throttleByTime(fn, times, ...args) {
let startTime = 0;
return function () {
const context = this;
let nowTime = Date.now();
if (nowTime - startTime > times) {
fn.call(context, ...args);
startTime = nowTime;
}
}
}
const addThrottleByTime = throttleByTime(add, 500);
document.addEventListener('mousemove', addThrottleByTime);
总结
相同点:防抖和节流的核心都是在一定的时间内,只能执行一次事件处理函数
不同点:
-
防抖是避免相同的操作执行多次
-
节流是降低高频事件触发的频次