函数防抖与节流

111 阅读3分钟

对“帕金森”say no !


/* debouce:防抖
 *  @params 
 *     - func : 最终要执行的函数
 *     - wait : 自定义频率 默认300ms
 *     - immediate : 设置触发边界,为true在开始边界除触发,默认在结束边界触发
 *  @return
 *     - func函数执行的返回结果
 *  update 2021/01/27 by qiancan
 */
 
 
const debounce = function debounce(func, wait, immediate) {
    if (typeof func !== 'function') throw new TypeError('func must be require and be a function!');
    if (typeof wait === 'boolean') {
        immediate = wait;
        wait = 300;
    }
    if (typeof wait !== 'number') wait = 300;
    if (typeof immediate !== 'boolean') immediate = false;

    let timer = null,
        result;
    return function proxy() {
        var runNow = !timer && immediate,
            params = [].slice.call(arguments),
            self = this;
        if (timer) clearTimeout(timer);
        timer = setTimeout(function () {
            if (timer) {
                clearTimeout(timer);
                timer = null;
            }
            !immediate ? result = func.apply(self, params) : null;
        }, wait);
        runNow ? result = func.apply(self, params) : null;
        return result;
    };
};

如果immediatefalserunnow就是falsefunc函数就是结束边界触发,反之为true在开始边界触发。

如果在300ms内,没有触发第二次点击,则只设定一个定时器,在wait时间后将func函数执行。并且将这个定时器也清除掉,同时赋值为null。如果不赋值为null,定时器虽然清了,但是timer还是有值的。如果开始边界执行,func将无法再次执行。func需要在开始边界执行,前提runnow需要为true。而runnowtrue的前提条件是timernull

如果300ms内发生多次点击,结果就是会产生很多的定时器,wait时间到后这些定时器一一执行。如果在300ms内第二次点击了,此时timer不为null,清除定时器操作执行。这一次重复点击生成的定时器会在下一次重复点击时清掉,直到wait时间不再发生多次点击为止。最后会留下一个定时器,也会被销毁。300ms开始边界触发同理。发生多次没次都会清除定时器,并且将最后剩下一个定时器执行时赋值为nullrunow才能让func再次执行。(当前有定时器runnow就是false

函数“变频器”

/* throttle:节流
 *  @params 
 *     - func : 最终要执行的函数
 *     - wait : 自定义频率 默认300ms
 *  @return
 *     - func函数执行的返回结果
 *  update 2021/01/27 by qiancan
 */
 
 const throttle = function throttle(func, wait) {
    if (typeof func !== 'function') throw new TypeError('func must be require and be a function');
    if (typeof wait !== 'number') wait = 300;

    var timer = null,
        previous = 0,
        result;
    return function proxy() {
        var self = this,
            params = [].slice.call(arguments),
            now = +new Date(),
            remaining = wait - (now - previous);
        if (remaining <= 0) {
            if (timer) {
                clearTimeout(timer);
                timer = null;
            }
            result = func.apply(self, params);
            previous = + new Date();
        } else if (!timer) {
            timer = setTimeout(function () {
                if (timer) {
                    clearTimeout(timer);
                    timer = null;
                }
                result = func.apply(self, params);
                previous = +new Date();
            }, remaining)
        }
        return result;
    };
};

控制触发频率。当设定频率内发生重复操作会被忽略,本来5ms后能执行第二次,现在让wait才能执行第二次。previous最初为0,用于保存当前这次点击的时间,用于和下一次的点击时间进行处理得到两次点击的时间差,拿两次时间差再去和wait判断第二次点击操作是否发生在wait时间内。如果时间差值比wait大,说明第二次点击发生在wait时间后,remaining小于等于0允许执行func。如果wait比时间差的时间大,说明第二次点击发生wait时间内=>设定定时器,过了remaining时间再去执行func。每次执行func,都要求把previous更新,比较最新的上一次。第一次func执行更新了previous。在第一次触发10毫秒后第二次触发了,和第一次时间差10ms,假设当前wait500ms,设置490ms定时器490ms后执行func。第二次点击生成的定时器在时间未到之前,previous未为被更新。当第二次相隔10ms,紧接着发生了第三次点击。得到的时间差还是和第一次的previous在比较,相当于500-20ms,又设置480ms定时器。490、480、470、460,只是在重复设定定时器。当没有达到间隔时间,同时之前没有定时器才设置定时器是合适的。定时器执行时清除定时器同时赋timernull,让下次能够重新设置定时器。