防抖与节流

90 阅读2分钟

防抖(debounce): 当持续触发事件时,一段时间没有再触发事件,事件触发函数才会执行一遍;如果设定时间还没到来之前,事件又被触发,就重新开始计时。

节流(throttle): 当持续触发事件时,保证在一定的时间内只触发一次事件处理函数。

常用的防抖节流库:underscore、lodash

防抖:

/**
 * 防抖函数,手动绑定this
 * @param fn 需要执行的函数
 * @param wait 执行需要等待的时间
 * @param immdiate 是否立即执行函数
 */
function debounce(fn, wait, immediate=false) {
    let timer = null;
    let ctx = null;
    const debounced = function(...rest) {
        ctx = this;
        if(timer) clearTimeout(timer);
        if(immediate && !timer) {
            fn.apply(ctx,rest);
            timer = setTimeout(() => {
                timer = null;
            }, wait)
        } else {
            timer = setTimeout(() => {            // 这里也可以直接使用this
                fn.apply(ctx, rest)
            }, wait)           
        } 
    };
    debounced.cancel = function() {
        if(timer) {
            clearTimeout(timer);
            timer = null;
        }
    }
    return debounced
}

节流:

1)采用时间戳,第一次会执行,最后一次不会执行

/**
 * 采用时间戳,
 * @param fn 实际需要执行的函数
 * @param wait 函数执行间隔时间
 * @options 一些额外选项,此方法用不上
 */
function throttle(fn, wait, options) {
    let old = 0;
    return function(...rest) {
        const now = Date.now();
        if(now - old > wait) {
            fn.apply(this, rest);
            old = now;
        }
    }
}

2) 采用定时器,第一不会执行,最后一次会执行

/**
 * 节流函数,采用定时器
 * @param fn 实际需要执行的函数
 * @param await 函数执行间隔的时间
 * @param options 一些额外的参数,此处不会用到
 */
function throttle(fn, await, options) {
    let timer = null;
    return function(...rest) {
        if(timer === null) {
            timer = setTimeout(() => {
                fn.apply(this, rest);
                timer = null
            }, wait)
        }
    }
}

3)同时采用时间戳和定时器,根据options中的选项,才确定第一和最后一次会不会执行

options.leading === true 则第一次会执行

options.trailing === true 则最后一次会执行

/**
 * 节流函数,综合使用时间戳和定时器
 * @param fn 实际要执行的函数
 * @param wait 函数执行需要间隔的时间
 * @param options 可配置参数,leading为true表示第一次执行,trailing为true表示最后一次执行
 * leading和trailing 不能同时为false
 **/
function throttle(fn, wait, options) {
    let old = 0;
    let timer = null;
    const leading = options.leading || false;
    const trailing = options.trailing || false;
    return function(...rest) {
        const now = Date.now();
        if(leading === false) {
            old = now
        }
        if(now - old > wait) {
            fn.apply(this, rest);
            old = now;
            if(timer) {clearTimeout(timer)}
            timer = null;
        }else if(trailing === true && timer === null) {
            timer = setTimeout(() => {
                fn.apply(this, rest);
                timer = null;
                old = now;
            }, wait)
        }
    }
}

// 以上代码参考underscore