更多个人博客:(https://github.com/zenglinan/blog)
如果对你有帮助,欢迎star。
防抖的原理: 为了避免事件频繁触发执行, 让事件只有在触发后 n 秒才执行, 假如 n 秒内重新触发了事件, 则刷新时间戳, 在下一个 n 秒后执行
使用方式大概是: dom.onmousemove = debounce(getUserAction, 1000);
基础版防抖: 输入指定时间, 实现防抖
function debounce(fn, time){
let timer
return function(){
clearTimeout(timer)
timer = setTimeout(fn, time)
}
}
只要在 time 内重新触发了事件, 定时器就会被清除掉, 重新开启下一个。
改良版
上一版实现了基础代码, 但是还有些地方需要完善, 比如:
- this 指向被改变了, 需要保证 fn 内部的 this 指向触发元素本身
- 需要提供 event 对象
这里为了保证 this, 采用箭头函数, 同时将 arguments 参数对象传入, 保证 event 对象可以获取到
function debounce(fn, time){
let timer
return function(){
// 事件触发时这里的 this 指向的是元素
let args = arguments // 取出的参数对象里包含了 event 对象
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, time)
}
}
立即执行
以上, 我们的防抖函数已经比较完备了
但是还可以添加一个需求使之更完善:
不要等到事件停止触发后才执行,我希望立刻执行函数,然后等到停止触发 n 秒后,才可以重新触发执行。
这里用一个可选参数来决定是否要采取这种立即触发的模式
function debounce(fn, time, immediate){
let timer
return function(){
let args = arguments
clearTimeout(timer)
if(immediate){ // 采取立即执行模式
let callNow = !timer // 只有 timer 为 falsy 值时, 才立即执行
timer = setTimeout(()=>{
timer = null // 只要 time 时间不触发, 就把 timer 设为 null, 那么下一次就会直接执行了
}, time)
callNow && fn.apply(this, args)
}
else{ // 采取传统模式
timer = setTimeout(() => {
fn.apply(this, args)
}, time)
}
}
}
思路剖析: 当传入第三个参数为 true 时, 开启立即执行模式, 第一次的时候 timer 肯定为 undefined, 这时候 callNow 为 true, 立即执行
与此同时, 给 timer 赋值定时器, 这时候 timer 就不是 falsy 值了, 如果马上再触发一次, !timer 为false, 不会马上执行
同时, timer 设置的定时器里, time 时间后就会把 timer 重置为 null, 但是每次触发都会清除这个定时器, 所以只要在 time 时间内不去触发, 再下次触发时, !timer 就会为 true, 就能立即执行了
要注意: 定时器返回给 timer 的是数字, 即使清除了定时器, timer 的值也不会被清除, 只有手动赋值才能让其为 falsy 值。
可取消
这里还可以再完善一下, 添加重置 debounce 函数的功能,
假如说 debounce 的时间间隔是 10 s, immediate 为 true, 我只有等 10 秒后才能重新触发事件
我希望有时能够立即重置防抖, 即回到立即执行的状态。
只需添加一个取消函数来重置 timer 即可
function debounce(fn, time, immediate){
let timer
var debounceFn = function(){
let args = arguments
clearTimeout(timer)
if(immediate){
let callNow = !timer
timer = setTimeout(()=>{
timer = null
}, time)
callNow && fn.apply(this, args)
}
else{
timer = setTimeout(() => {
fn.apply(this, args)
}, time)
}
}
debounceFn.reset = function(){ // 取消函数, 注意: 取消完后又恢复
clearTimeout(timer)
timer = null
}
return debounceFn
}
使用的时候需要这样使用
let debounced = debounce(fn, 1000)
dom.onmousemove = debounced
// 重置函数的使用方法
button.onclick = debounced.reset
网友评论