JS防抖函数实现

193 阅读2分钟

需要防抖函数出现的业务场景与需求:当某个事件,如点击按钮、滚动/缩放浏览器时,在规定的时间内多次触发事件的回调函数,需要只真正执行回调函数一次

实际案例:如支付时,当用户多次点击支付按钮后,在规定实际内发起支付成功请求,每笔订单应该只支付成功一次

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;
            })
        } 
    }
}