持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情
防抖
触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次触发,则重新计算时间。
版本【一】:基础功能
- 每次触发事件时,都取消之前的延时调用方法
function debounce(fn, wait = 500) { // wait:防抖时间,默认500ms
let timer = null;
return function() {
if(timer) clearTimeout(timer); // 每当函数调用的时候,把前一个定时器清理掉
timer = setTimeout(func, wait)
}
}
版本【二】:绑定this
- 如果我们需要做防抖处理的函数,内部使用了
this的话,根据 “回调函数中的this指向window” ,则fn函数中 的this指向会出问题
我们使用 apply 来解决这个问题
function debounce(fn, wait = 500) {
let timer = null;
return function() {
if(timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this); // 这里使用了箭头函数,所以这里的 this 和 return function 中的 this 相同
}, wait)
}
}
function sayHi() {
console.log("防抖");
}
var input = document.getElementById("input");
// 我们最终调用的时候,是绑定的 debounce 返回的函数,所以 debounce “返回的函数” 中 this 指向 input 的DOM结构
input.addEventListener("input", debounce(sayHi)); // 防抖
版本【三】:传参
- 我们自定义的事件可能会有参数传递
- js原生的事件处理函数会传递
event对象
function debounce(fn, wait = 500) {
let timer = null;
return function(...args) { // 在这里捕获入参
if(timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args); // 传递给 apply 的第二个参数
}, wait)
}
}
版本【四】:立即执行
有的时候,我们不希望等到事件停止触发后才执行,而是希望首次触发的时候立即执行,而后再进行防抖逻辑
function debounce(fn, wait = 500, immediate = false) { // 使用immediate判断是否需要立即执行
let timer = null;
return function(...args) {
if(timer) clearTimeout(timer);
if(immediate) {
// 如果已经执行过了,则不需要再次执行
let canRun = !timer;
timer = setTimeout(() => {
timer = null; // 将定时器设置为 null,高频调用函数结束(短时间内的高频结束了)。保证下一次“高频调用”出现时,可以再次“立即执行”
}, wait)
// 如果 timer 当前为 null,则说明这是本次“高频调用”的第一次执行,那么就“立即执行”
if(canRun) fn.apply(this, args);
} else {
// 原先的逻辑
timer = setTimeout(() => {
fn.apply(this, args);
}, wait)
}
}
}
版本【五】:取消函数执行
假设我们的防抖时间间隔是 5s,但是在这 5s 内(函数尚未执行),我突然不想让这个函数执行了(点错了?),那么应该如何处理呢?
debounce 函数目前返回了一个函数,那么我们可以考虑给这个函数上再挂载一个 cancel 函数
- 将之前的代码改造为如下形式(效果一致)
function debounce(fn, wait = 500, immediate = false) {
let timer = null;
let debounced = function(...args) {
if(timer) clearTimeout(timer);
if(immediate) {
let canRun = !timer;
timer = setTimeout(() => {
timer = null;
}, wait)
if(canRun) fn.apply(this, args);
} else {
timer = setTimeout(() => {
fn.apply(this, args);
}, wait)
}
}
return debounced
}
- 再对
debounced进行挂载额外的函数
function debounce(fn, wait = 500, immediate = false) {
let timer = null;
let debounced = function(...args) {
if(timer) clearTimeout(timer);
if(immediate) {
let canRun = !timer;
timer = setTimeout(() => {
timer = null;
}, wait)
if(canRun) fn.apply(this, args);
} else {
timer = setTimeout(() => {
fn.apply(this, args);
}, wait)
}
}
debounced.cancel = function() {
clearTimeout(timer); // 清除定时器
timer = null; // 方便下次“立即执行”
}
return debounced
}
- 调用 debounce
function sayHi() {
console.log("防抖");
}
var input = document.getElementById("input");
var debounceSayHi = debounce(sayHi);
input.addEventListener("input", debounceSayHi);
// 取消操作
var button = document.getElementById("button");
button.addEventListener("click", function() {
debounceSayHi.cancel();
})