Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

防抖与节流 #29

Open
dark9wesley opened this issue May 20, 2021 · 0 comments
Open

防抖与节流 #29

dark9wesley opened this issue May 20, 2021 · 0 comments

Comments

@dark9wesley
Copy link
Owner

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

  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学节流
函数防抖和节流

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant