Skip to content

防抖与节流 #29

Open
Open
@dark9wesley

Description

@dark9wesley

在前端开发中,某些场景下会遇到一些频繁的事件触发,比如:

  1. window 的 resize、scroll
  2. mousedown、mousemove
  3. keyup、keydown

如果这些频繁的事件触发后是一些复杂的回调操作,那么就会导致性能浪费,甚至程序崩溃。

防抖和节流的出现就是为了遏制频繁触发的事件回调。

防抖

防抖的原理就是:事件触发n秒后才执行回调,如果这n秒内事件再次触发,重新计时。

防抖分为非立即执行版本和立即执行版本。

非立即执行版防抖

非立即执行防抖就是传统的防抖,事件触发n秒后才执行回调函数。

实现如下:

function debounce(fn, wait){
    let timeout;

    return function(){
        let args = [...arguments];

        //定时器存在,也就是还处于n秒内,清楚该定时器,在下面重新计时
        if(timeout) clearTimeout(timeout);

        timeout = setTimeout(() => {
            fn.apply(this, args)
        }, wait);
    }
}

立即执行版防抖

立即执行防抖就是事件第一次触发时马上执行回调,然后隔n秒后才可以触发第二次,如果这n秒内事件再次触发,重新计时。

实现如下:

function debounce(fn, wait){
    let timeout;

    return function(){
        let args = [...arguments];

        //定时器存在,也就是还处于n秒内,清除该定时器,在下面重新计时
        if(timeout) clearTimeout(timeout)

        //定时器存在,也就是还处于n秒内,不能执行回调
        let callNow = !timeout;
        
        timeout = setTimeout(() => {
            timeout = null;
        }, wait)

        if(callNow) fn.apply(this, args);
    }
}

节流

节流的原理就是:事件频繁触发但是n秒内只会执行一次回调函数,也就是会稀释事件回调的执行频率

关于节流的实现,有两种实现方式,一种是使用时间戳,一种是设置定时器。

时间戳版节流

原理就是设立一个时间戳,当事件触发时取出当前的时间戳,然后减去之前的时间戳(最一开始值设为0),如果大于设立的时间周期,就执行函数并更新时间戳为当前的时间戳,如果小于就不执行。

实现如下:

function throttle(fn, wait){
    let time = 0;

    return function(){
        let now = new Date();
        let args = [...arguments];

        if(now - time > wait){
            fn.apply(this, args);
            time = now;
        }
    }
}

由于第一次进入time默认是0,now - time第一次都会大于规定的时间(除非传入一个特别大的时间),所以第一次事件回调立刻执行,当停止触发事件后,不会再执行回调。

定时器版节流

原理就是设立一个定时器,定时器存在时就不执行回调,当事件触发时会启动定时器,定时器到期后会把自己赋值为null。

实现如下:

function throttle(fn, wait){
    let timeout;

    return function(){
        let args = [...arguments];
        if(!timeout){
            timeout = setTimeout(() => {
                timeout = null;
                fn.apply(this, args)
            }, wait)
        }
    }
}

由于基于定时器,所以会有延后执行的特性,第一次事件触发时不会立即执行,事件停止触发后过一段时间还会执行一次。

需要注意的点

  • 注意this的指向,如果不用apply会导致fn的this指向window。
  • JS在事件处理函数中会提供事件对象event,要将这个事件对象传递给fn。

参考

JavaScript专题之跟着underscore学防抖
JavaScript专题之跟着underscore学节流
函数防抖和节流

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions