函数的防抖(debounce)和节流(throttle)

220 阅读2分钟

在"高频"触发的场景下,需要进行防抖和节流

  1. 狂点一个按钮;
  2. 页面滚动;
  3. 输入模糊匹配....

我们自己设定,多长的时间内,触发两次及以上就算“高频”:封装方法的时候需要指定这个频率(可以设置默认值)

【防抖】在某一次高频触发下,我们只识别一次(可以控制开始触发,还是最后一次触发);详细:假设我们规定500MS触发多次算是高频,只要我们检测到是高频触发了,则在本次频繁操作下(哪怕你操作了10min)也是只触发一次...

【节流】在某一次高频触发下,我们不是只识别一次,按照我们设定的间隔时间(自己规定的频率),每到达这个频率都会触发一次;详细:假设我们规定频率是500MS,我们操作了10min,触发的次数=(10x60x1000)/500

【防抖】

业务场景中处理的技巧1:标识判断

let flag=false;
submit.onclick=function(){
    if (flag) return;
    flag=true;
    setTimeout(()=>{
        flag=false;
    },1000)
}

业务场景中处理的技巧2:按钮重置为灰色,移除事件绑定

function handle(){
    submit.onclick=null;
    submit.disabled=true;
    setTimeout(()=>{
        submit.onclick=handle;
        submit.disabled=false;
    },1000)
}
submit.onclick=handle;

封装一个公共函数解决疯狂点击执行多次问题

function debounce(func,wait,immediate){
    //多个参数及传递默认的处理
    if(typeof func !== 'function') throw new TypeError('func must be an function!')
    if(typeof wait === 'undefined') wait 500;
    if(typeof wait === 'boolean'){
        immediate=wait
        wait=500
    }
    if(typeof immediate !== 'boolean') immediate=false;

    //设定定时器返回值标识
    let timer = null;
    return function proxy(...params){
        let self=this,
        now = immediate && !timer;

        clearTimeout(timer);
        timer=setTimeout(function(){
            timer=null;
            !immediate?func.call(self,...params) : null;
        },wait)

        //第一次触发就立即执行
        now ? func.call(self,...params):null
    }
}

function handle(ev){
    //具体在点击时要处理的业务

}

submit.onclick=debounce(handle,true)

【节流】

节流一般用于页面滚动和输入模糊匹配

function throttle(func,wait){
    if(typeof func!=='function') throw new TypeError('func must be an function!')
    if(typeof wait === 'undefined') wait = 500
    let timer =null,
    previous = 0;//记录上一次操作的时间
    return function proxy(...params){
        let self = this,
            now = new Date(),//当前这次触发操作的时间
            remaining = wait - (now-previous);
        if(remaining <=0 ){
            //两次间隔时间超过wait了,直接执行即可
            clearTimeout(timer);
            timer=null;
            previous = now;
            func.call(self,...params);
        }else if(!timer){
            //两次触发的间隔时间没有超过wait,则设置定时器,让其等待remaining这么久之后执行一次【前提:没有设置过定时器】
            timer=setTimeout(function(){
                clearTimeout(timer);
                timer=null;
                previous=new Date();
                func.call(self,...params);
            },remaining);
        }
    }
}

function handle(){
    console.log('ok)
}
window.onscroll=throttle(handle);