一、什么是防抖和节流?
函数防抖和节流,都是控制事件触发频率的方法。
防抖:假设函数持续多次执行,我们希望让它冷静下来再执行。也就是当持续触发事件的时候,函数是完全不执行的,等最后一次触发结束的一段时间之后,再去执行。
节流:让函数有节制地执行,而不是毫无节制的触发一次就执行一次。
什么叫有节制呢?就是在一段时间内,只执行一次。
节流顾名思义则是将减少一段时间内触发的频率,指定时间间隔内只会执行一次任务。
防抖场景
- 文本输入的验证,连续输入文字后发送 AJAX 请求进行验证,验证一次就好。
- 监听一个输入框的,文字变化后触发change 事件
- 直接用keyup 事件,则会频发触发change事件
- 防抖:用户输入结束或暂停时,才会触发change事件
- 用户快速点击翻页时,只需要执行用户最后一次希望到达的页数即可,中间页数都是用户不希望看到的,使用防抖函数可以有效的减少发送请求的次数
节流场景
-
懒加载要监听计算滚动条的位置,使用节流按一定时间的频率获取
-
DOM 元素的拖拽功能实现(mousemove)
- 拖拽一个元素时,要随时拿到该元素被拖拽的位置
- 直接用drag事件,则会频繁触发,很容易导致卡顿
- 节流:无论拖拽速度多快,都会每隔100ms触发一次
二、初步实现
防抖
const input = document.querySelector('#input');
let timer = null;
input.addEventListener('keyup', function () {
clearTimeout(timer);
timer = setTimeout(() => {
// 模拟触发change事件
console.log(input.value);
}, 500);
});
节流
const div1 = document.getElementById('div1');
let timer = null;
div1.addEventListener('drag', e => {
if (timer) {
return;
}
timer = setTimeout(() => {
console.log(e.offsetX, e.offsetY);
timer = null;
}, 100);
});
三、函数封装
防抖封装
function debounce(fn, delay = 500) {
// timer 是在闭包中的
let timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, delay);
};
}
input.addEventListener(
'keyup',
debounce(function () {
console.log(input.value);
})
);
节流封装
div1.addEventListener(
'drag',
throttle(function (e) {
console.log(e.offsetX, e.offsetY);
})
);
function throttle(fn, delay = 100) {
let timer = null;
return function (e) {
if (timer) {
return;
}
timer = setTimeout(() => {
fn.apply(this, arguments);
// fn(e)
timer = null;
}, delay);
};
}