防抖和节流的原理与实现

743 阅读3分钟
  • 防抖和节流用来都是用来控制事件的触发的次数,减少触发频率提升网站性能。

防抖

  1. 防抖:定义时间t,在t时间后执行函数,在t时间内如果事件重复触发,则重新计时
  2. 立即执行和非立即执行:在事件触发时先执行函数再开始计时;先开始计时,时间到了再执行函数

综合以上两点实现防抖函数:

传入三个参数:

func:需要防抖的函数

delay:防抖的时间

immediate:是否立即执行,这里默认为立即执行

使用了在ts项目中的例子:

export default function debounce(func:Function,delay=100,immediate=true){
    let timer:NodeJS.Timeout|null;
    return function(this:any,...args:Array<any>){
        const context = this;
        //防抖函数的关键,在timer还存在时如果触发了事件,则清空定时器,重新计时
        if(timer) clearTimeout(timer);
        //判断是否需要立即执行
        if(immediate){
            if(!timer) func.apply(context,args);
            timer = setTimeout(()=>{
                timer = null;
            },delay)
        }else{
            timer = setTimeout(() => {
                func.apply(context,args)
            }, delay);
        }
    }
}

节流

  1. 节流:定义一个时间t,在t时间内只允许触发一次事件
  2. 首节流和尾节流:事件触发时是否立即执行,与防抖中的immediate类似

基于时间戳的首节流函数:

function throttle(fn,delay=1000){
    let preTime = 0;
    return function(...args){
        let now  = new Date();
        let context = this;
        let res;
        if(now-preTime>=delay){
            res = fn.call(context,...args);
            preTime = new Date();
        }
        return res
    }
}

基于计时器的尾节流:

// 定时器 尾节流
function throttle2(fn,delay=1000){
    // timeout一定要放在外面,否则每次运行都会创建一个timeout。
    let timeout = null;
    return function(...args){
        let context = this;
        let res;
        if(!timeout){
            timeout = setTimeout(()=>{
                res = fn.call(context,...args);
                timeout = null;
            },delay)
        }
        return res;
    }
}

结合前二者的首尾节流:

首尾节流可以实现当事件触发时立即执行,事件最后一次触发后延迟一段时间执行

function throttle3(fn,delay=1000){
    let timeout = null;
    let preTime = 0;
    return function(...args){
        let context = this;
        let remain = 0;
        let now = new Date();
        let res;
        clearTimeout(timeout);
        remain = delay - (now-preTime);
        //如果remain小于0则进行首节流
        if(remain<=0){
            res = fn.call(context,...args);
            preTime = new Date();
        }else{
            timeout = setTimeout(()=>{
                res = fn.apply(context,args);
            },delay)
        }
        return res;
    }
}

防抖和节流的区别与用途

区别:

  1. 体现在运行机制的区别:
    • 防抖中一个事件在t时间内重复触发,计数器会重新计时
    • 节流事件在t时间内触发时不会重新计时,而是选择不予理会
  2. 体现在用途上的区别:
    • 节流函数体现的是减少触发次数,而不是让触发次数保持为1次,如鼠标移动、窗口变换、元素拖拽等事件,需要减少对这些事件的响应次数减少资源浪费,但是不能只响应一次
    • 防抖则体现的是一种"等待"的效果,如输入验证、动态搜索、文本翻译等,需要"等待"用户把该输入的输入完成了再执行,如果用户在t时间内没有输入完成,则判断为需要继续等待用户,所以重新计时