Skip to content

函数的防抖和节流

作用

防抖:触发事件后,不立即执行,等待 N 毫秒。如果 N 毫秒内再次触发,则重新计时。

节流:固定时间内只执行一次。

防抖和节流的区别

对比防抖节流
英文DebounceThrottle
核心只执行最后一次固定时间执行一次
连续触发不执行周期执行
停止触发执行一次不一定
适用场景搜索框scroll、resize

手写函数的防抖和节流

js
/* 函数的防抖和节流 */
const clearTimer = function clearTimer(timer) {
    if (timer) clearTimeout(timer);
    return null;
};

// 函数防抖:防止“老年帕金森”,用户频繁触发某个行为的时候,我们只识别“一次”「频繁的定义可以自己管控」
const debounce = function debounce(func, wait, immediate = false) {
    // init params
    if (typeof func !== "function") throw new TypeError("func is not a function!");
    if (typeof wait === "boolean") {
        immediate = wait;
        wait = undefined;
    }
    wait = +wait;
    if (isNaN(wait)) wait = 300;
    if (typeof immediate !== "boolean") immediate = false;
    // handler
    let timer = null;
    return function operate(...params) {
        // now:记录是否是立即执行「第一次点击&immediate=true」
        let now = !timer && immediate;
        // 清除之前设置的定时器
        timer = clearTimer(timer);
        timer = setTimeout(() => {
            // 结束边界触发
            if (!immediate) func.call(this, ...params);
            // 清除最后一个定时器
            timer = clearTimer(timer);
        }, wait);
        // 如果是立即执行,则第一次执行operate就把要干的事情做了即可 “开始边界触发”
        if (now) func.call(this, ...params);
    };
};

// 时间戳版适合首次立即响应,定时器版适合保证最后一次执行。实际项目中通常采用两者结合的方式,同时支持 leading 和 trailing
// 时间戳+定时器结合版本
// 函数节流:用户频繁操作的时候,不根据用户的频繁操作度来绝定触发多少次,而是根据设定好的频率进行触发,实现“降频”的效果,相对于防抖来讲,节流是允许触发多次的
const throttle = function throttle(func, wait) {
    // init params
    if (typeof func !== "function") throw new TypeError("func is not a function!");
    wait = +wait;
    if (isNaN(wait)) wait = 300;
    // handler
    let timer = null,
        previous = 0; //记录上一次func触发的时间
    return function operate(...params) {
        let now = +new Date(),
            remaining = wait - (now - previous);
        if (remaining <= 0) {
            // 两次触发的间隔时间超过设定的频率,则立即执行函数
            func.call(this, ...params);
            previous = +new Date();
            timer = clearTimer(timer);
        } else if (!timer) {
            // 间隔时间不足设定的频率,而且还未设置等待的定时器,则设置定时器等待执行函数即可
            timer = setTimeout(() => {
                func.call(this, ...params);
                previous = +new Date();
                timer = clearTimer(timer);
            }, remaining);
        }
    };
};

Released under the MIT License.