什么是防抖(Debounce)?
短时间内连续触发多次事件(用户点击/鼠标移动/键盘事件/鼠标滚轮等),只执行最后一次, 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
javascript
var btn = document.getElementById("btn");
btn.onclick = debounce(() => {
console.log("防抖执行了");
}, 1000);
//封装防抖函数(debounce)
function debounce(fn, wait) {
let timer = null;
return function () {
let _this = this;
let args = arguments;
// 每次点击时都是先关在开,永远保证当前只有一个正在执行的定时器
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
fn.apply(_this, args);
}, wait);
console.log(timer);
};
}
下图是控制台的输出情况:
可以发现,只有当用户停止点击后,才会触发fn()函数,打印"防抖执行了"语句,每次产生新的触发,都会覆盖旧有触发的定时器,并重新计时,即多次点击只有计最后一次有效。
什么是节流(Throttle)?
在一个单位时间内,连续多次事件触发,只执行第一次。短时间内连续触发多次事件,会减少执行次数。达到稀释函数执行频率的效果。 可以将节流函数看作一个闸门,一旦有事件触发,就立即关闭闸门,不让其他事件进入,直到进入的事件响应结束,才再次开放闸门。
1.用定时器实现:
javascript
var btn = document.getElementById("btn");
btn.onclick = throttle(() => { console.log("节流函数执行"); }, 1000);
function throttle(fn, wait) {
let timer = null;
return function () {
let _this = this;
let _args = arguments;
if (!timer) { //若当前没有正在执行的定时器
timer = setTimeout(function () {
timer = null;
fn.apply(_this, _args);
}, wait); //只有当定时器将wait时间走完,timer才会再次被赋值null,才可再次进入if语句
}
console.log(timer);
};
}
2.用时间戳实现
javascript
function throttle1(fn, wait) {
let time = 0;
return function () {
let _this = this;
let _args = arguments;
let now = Date.now();
//相邻两次点击的时间间隔大于需要wait的时间,即上一单位时间内事件已响应完毕
if (now - time > wait) {
fn.apply(_this, _args);
time = now;
}
};
下图是控制台的输出情况:
可以发现,与防抖函数不同,当用户多次点击时,实际响应的始终是第一次点击时的定时器。只至定时器将需要wait的时间走完,打印了"节流函数执行"。即单位时间内,多次触发仅执行第一次。
防抖和节流的异同
相同点:
- 作用都是为了降低回调函数触发频率,减小计算负担。
- 都是基于一次性定时器setTimeout实现的,并运用闭包进行封装。
区别:
防抖函数多次触发只执行最后一次,节流函数多次触发只执行第一次。
触发频率不同,实际上防抖函数比节流函数更为"严格",防抖在单位时间s内永远只执行最后一次,而节流函数函数则可执行s/wait(向下取整)次。
例如,防抖和节流函数定时器的wait时间均为500ms,那么在5s内,防抖函数仅触发最后一次,而节流函数将触发10次。
防抖和节流的使用场景
防抖(适合仅需一次生效的场景)
- 登录,发送验证码,限时秒杀等按钮,用户短时间内多次点击,仅最后一次响应并发送请求
- search搜索时,等待用户结束输入后,再发送请求。
- windows触发resize时,会频繁进行计算调整窗口大小,用防抖节约计算资源。
节流(适合隔一段时间执行一次场景)
- 滚动监听,例如下拉加载,触底加载等
- 搜索框的搜索联想功能