防抖函数
防抖应用场景
- 点击按钮发送消息, 避免用户快速点击发送多个请求.
- 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
- 文本编辑器实时保存,当无任何更改操作一秒后进行保存
- …
特性
- 极其简单
- this正确指向。实现作为事件回调函数,this的指向仍然是被绑定的事件的元素。event事件对象正确传递。
- 增加立即执行功能。第一次触发(点击,输入等)会立即执行,后面在delay区间的触发会作为一次触发。
- 可取消。返回cancel函数,执行即可取消此次触发的事件。场景:点击之后用户又立即点击了返回按钮,或点击返回首页,此次操作取消,不必再发送此次网络请求。
- 返回执行函数的返回值。
正文
基础防抖函数
使用input输入框演示
const ipt = document.querySelector('input');
ipt.addEventListener('input', debounce(onInput, 300))
function onInput(event) {
console.log(123, this, event);
}
function debounce(fn, delay = 500) {
let timer = null;
const _debounce = function () {
clearTimeout(timer); // 闭包
timer = setTimeout(() => {
fn();
}, delay);
}
return _debounce;
}
this的指向正确与Event事件对象正确传递
function debounce(fn, delay = 500) {
let timer = null;
// 2.执行此debounce函数,返回一个函数,这个函数作为addEventListener传入的第二个参数,event对象自然也就传给了这个函数.用一个形参将其接收,再将其传给正真回调的函数.
const _debounce = function (...agrs) {
clearTimeout(timer); // 闭包
timer = setTimeout(() => {
// 这个函数作为 被监听元素 的回调函数, 该函数的this就会指向被监听元素元素.
// 1.箭头函数不绑定this,自动去它的上层作用域找, 外层就是addEventListener传入的第二个参数,一个回调函数,它的this是正确的,指向的是触发事件的元素,绑定它即可.
fn.apply(this, agrs);
}, delay);
}
return _debounce;
}
首次事件触发立即执行
// 增加参数immediate默认false
function debounce(fn, delay = 500, immediate = false) {
let timer = null;
// 增加自己的控制参数.
// 依靠immediate也能做到. 尽量不要操作外界传入的东西.
let isInvoke = false;
const _debounce = function (...agrs) {
// 判断
if (immediate && !isInvoke) {
fn.apply(this, agrs);
// 已经调用一次
isInvoke = true;;
} else {
clearTimeout(timer); // 闭包
timer = setTimeout(() => {
fn.apply(this, agrs);
// delay时间到了执行完函数后改为false, 下次继续执行 第一次触发直接调用函数
isInvoke = false;
}, delay);
}
}
return _debounce;
}
返回cancel函数
function debounce(fn, delay = 500, immediate = false) {
let timer = null;
let isInvoke = false;
const _debounce = function (...agrs) {
if (immediate && !isInvoke) {
fn.apply(this, agrs);
isInvoke = true;
} else {
clearTimeout(timer); // 闭包
timer = setTimeout(() => {
fn.apply(this, agrs);
isInvoke = false;
}, delay);
}
}
// 函数既是函数又是对象, 给他再加一个cancel函数.
_debounce.cancel = () => {
if (timer) clearTimeout(timer);
}
return _debounce;
}
返回函数执行的返回值 & 最终形态
function debounce(fn, delay = 500, immediate = false, resultCallback = null) {
let timer = null;
let isInvoke = false;
function _debounce(...agrs) {
return new Promise((resolve, reject) => {
if (immediate && !isInvoke) {
const result = fn.apply(this, agrs);
if (resultCallback) resultCallback(result);
resolve(result)
isInvoke = true;
} else {
clearTimeout(timer); // 闭包
timer = setTimeout(() => {
// 这个作为 元素input 的回调函数, 该函数的this就会指向input元素.
// 箭头函数不绑定this,自动去它的上层作用域找, 外层就是addEventListener传入的第二个参数,一个函数,它的this是正确的,指向的是触发事件的元素.
const result = fn.apply(this, agrs);
if (resultCallback) resultCallback(result);
resolve(result)
isInvoke = false;
}, delay);
}
})
}
_debounce.cancel = () => {
if (timer) clearTimeout(timer);
timer = null;
}
return _debounce;
}