1、什么是防抖
短时间内大量触发同一事件,只会执行一次函数。
函数防抖(debounce),指触发事件后在一定时间(比如 3 秒)内函数只能执行一次,如果这段时间(3 秒)内又触发了事件,则会重新计算函数执行时间。
2、为什么要防抖
我们在滚动屏幕的时候,一秒可以轻松出发多次事件,如果每次事件都请求数据,而且如果事件处理逻辑复杂,浏览器的压力会很大,输入框、滚动条的监听事件处理,如果不做防抖,每输入一个字或者滚动一下屏幕,都会请求数据,这样会造成性能浪费,用户体验也会很糟糕。
3、防抖原理
设置一个定时器,约定在n毫秒后再触发事件处理,每次触发事件都会重新设置定时器,直到n秒内无第二次触发操作时,再执行函数。
- 第一次调用函数,创建一个定时器,在指定时间间隔之后运行代码
- 第二次调用函数,清除前一次的定时器,
- 如果前一个已经执行过了,则没有意义,如果尚未执行,将其替换成一个新的定时器,目的是只有在执行函数的请求停止了一段时间之后才执行。
4、手写防抖
function debounce(method, wait, immediate) {
let timeout
// debounced函数为返回值
// 使用Async/Await处理异步,如果函数异步执行,等待setTimeout执行完,拿到原函数返回值后将其返回
// args为返回函数调用时传入的参数,传给method
let debounced = function(...args) {
return new Promise (resolve => {
// 用于记录原函数执行结果
let result
// 将method执行时this的指向设为debounce返回的函数被调用时的this指向
let context = this
// 如果存在定时器则将其清除
if (timeout) {
clearTimeout(timeout)
}
// 立即执行需要两个条件,一是immediate为true,二是timeout未被赋值或被置为null
if (immediate) {
// 如果定时器不存在,则立即执行,并设置一个定时器,wait毫秒后将定时器置为null
// 这样确保立即执行后wait毫秒内不会被再次触发
let callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
// 如果满足上述两个条件,则立即执行并记录其执行结果
if (callNow) {
result = method.apply(context, args)
resolve(result)
}
} else {
// 如果immediate为false,则等待函数执行并记录其执行结果
// 并将Promise状态置为fullfilled,以使函数继续执行
timeout = setTimeout(() => {
// args是一个数组,所以使用fn.apply
// 也可写作method.call(context, ...args)
result = method.apply(context, args)
resolve(result)
}, wait)
}
})
}
// 在返回的debounced函数上添加取消方法
debounced.cancel = function() {
clearTimeout(timeout)
timeout = null
}
return debounced
}
简单版
function debounce(func, wait) {
let timeout = null
return function() {
let context = this
let args = arguments
if (timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
func.apply(context, args)
}, wait)
}
}