简单实现
<input type="text">
<script src="./01_debounce-v1-基本实现.js"></script>
<script>
const inputEl = document.querySelector("input")
let counter = 0
const inputChange = function(event) {
console.log(`发送了第${++counter}次网络请求`)
}
// 防抖处理
inputEl.oninput = debounce(inputChange, 2000)
</script>
function debounce(fn, delay) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null
// 2.真正执行的函数
const _debounce = function() {
// 取消上一次的定时器
// (不取消就会在最后一次性发送前面堆积的请求,而我们要实现的是到最后才请求一次)
if (timer) clearTimeout(timer)
// 延迟执行
timer = setTimeout(() => {
// 外部传入的真正要执行的函数
fn()
}, delay)
}
return _debounce
}
功能优化——this、参数改进
在原来的基础之上将inputChange修改了一下:
<script>
const inputChange = function(event) {
console.log(`发送了第${++counter}次网络请求`,this,event)
}
</script>
直接用inputEl.oninput = inputChange
触发得到:
用第三方库触发得到:
用我们上面手写的防抖函数得到:
可见的我们的函数对this和event的处理是有问题的。
改进:
function debounce(fn, delay) {
let timer = null
const _debounce = function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
// 用apply进行绑定this、arg
fn.apply(this, args)
}, delay)
}
return _debounce
}
功能优化——立即执行
:::info
功能实现:希望在第一次执行的时候就能给我立刻请求一次(可以选择立即执行/不立即执行)
:::
改进:
function debounce(fn, delay, immediate = false) {
let timer = null
let isInvoke = false
const _debounce = function(...args) {
if (timer) clearTimeout(timer)
// 判断是否需要立即执行
if (immediate && !isInvoke) {
fn.apply(this, args)
isInvoke = true
} else {
timer = setTimeout(() => {
fn.apply(this, args)
isInvoke = false
}, delay)
}
}
return _debounce
}
功能优化——取消功能
:::info
场景:用户在输入东西之后防抖函数发送请求之前按到退出键退出了界面,这时我们就不用再发送请求
:::
<script>
// 取消功能
const cancelBtn = document.querySelector("#cancel")
cancelBtn.onclick = function() {
debounceChange.cancel()
}
<script>
// 封装取消功能
_debounce.cancel = function() {
if (timer) clearTimeout(timer)
// 好习惯:取消或执行完毕后将默认值重置
timer = null
isInvoke = false
}
功能优化——函数返回值
<script>
const inputChange = function(event) {
console.log(`发送了第${++counter}次网络请求`, this, event)
// 返回值
return "aaaaaaaaaaaa"
}
// 防抖处理
const debounceChange = debounce(inputChange, 3000, false, (res) => {
console.log("拿到真正执行函数的返回值:", res)
})
</script>
function debounce(fn, delay, immediate = false, resultCallback) {
let timer = null
let isInvoke = false
const _debounce = function(...args) {
if (timer) clearTimeout(timer)
if (immediate && !isInvoke) {
const result = fn.apply(this, args)
if (resultCallback) resultCallback(result)
isInvoke = true
} else {
timer = setTimeout(() => {
const result = fn.apply(this, args)
if (resultCallback) resultCallback(result)
isInvoke = false
timer = null
}, delay)
}
})
}
// 封装取消功能
_debounce.cancel = function() {
if (timer) clearTimeout(timer)
timer = null
isInvoke = false
}
return _debounce
}