需要防抖函数出现的业务场景与需求:当某个事件,如点击按钮、滚动/缩放浏览器时,在规定的时间内多次触发事件的回调函数,需要只真正执行回调函数一次
实际案例:如支付时,当用户多次点击支付按钮后,在规定实际内发起支付成功请求,每笔订单应该只支付成功一次
const button = document.querySelector('button');
// 需要采用防抖控制的业务代码
let payMoney = function payMoney(){
console.log('点击生效');
// 打印调用该函数的this上下文及payMoeny的arguments
console.log(this,arguments);
}
// 防抖函数,func参数代表业务代码
function debounce(func, delay = 100){
// 定义timer在闭包外,闭包内函数调用都是同一个timer(作用域链)
let timer;
return function(){
let context = this;
let args = arguments;
// 每次点击清空之前的点击(未到规定时间)Timer的赋值
clearTimeout(timer);
// 2.每次匿名函数会去找本地执行上下文是否有timer的引用(引用一次就可以了),如果没有则向上一层执行上下文(外部函数debounce)查找
timer = setTimeout(()=>{
func.call(context, args);
// 每次成功调用业务代码,清空timer的引用,以免造成内存泄漏
timer = null;
}, delay);
// 题外,内部函数调用外部函数的变量,要将其置为null,防止内存泄漏
//(外部函数执行完了,变量自动销毁;内部函数之前还有引用,应该主动销毁[只会自动销毁自己的变量])
// 3.这里不能置为null,置为null后会每次向上一层执行上下文引用timer,与在内部匿名函数内定义let timer一样,
// 3.每次点击有新的timer(timer=null 指的是下次又有新的引用),类似于多线程,无法做到防抖,可以把timer=null放到setTimeout里
//timer = null;
}
}
// 1.为点击事件添加防抖函数回调,debounce函数实际执行匿名函数的上下文(每个函数有自己的上下文,最大的是全局上下文)
button.addEventListener('click',debounce(payMoney, 1000));
—————————————————————————————————————————
Vue项目实现防抖(疑惑点:不知道为何不能在方法中设置返回内部匿名 [闭包] 函数?)
export default{
...
data(){
return {
...
timer: null;
};
},
...
methods:{
submitDebounce(){
clearTimer(this.timer);
this.timer = setTimeout(() => {
// 业务代码
this.subAddr();
timer = null;
})
}
}
}