节流函数及其使用示例

3,250 阅读1分钟

一、节流函数的使用场景及类型

  1. 使用场景:用于需要执行多次且触发相对平滑的情况,单位时间内事件只能触发一次,如:scroll 事件、mouseover事件、播放事件等。

  2. 类型:节流函数分为立即执行和非立即执行两种,立即执行的为前缘节流,非立即执行的为延迟节流。

二、节流函数

  1. 前缘节流
// 前缘节流,首次触发立即生效,后续需要间隔一段时间触发才生效
function throttleImmediateExecution(fn, delay){
    let timer = null; // 使用定时器实现
    return function (){
        let _this = this; // 当前 this 保存,以免后续处理中 this 丢失
        if(!timer){
            fn.apply(_this, arguments);
            timer = setTimeout(function (){
                timer = null;
            }, delay);
        }
    }
}
  1. 延迟节流
// 延迟节流,首次及后续都需要间隔一段时间触发才生效
function throttleDelayExecution(fn, delay){
    let last = Date.now(); // 使用时间戳实现
    return function (){
        let now = Date.now();
        if(now - last >= delay){
            fn.apply(this, arguments);
            last = now;
        }
    }
}
  1. 可选前缘或延迟节流
// 节流完整版,可选前缘节流或延迟节流,个人感觉这个最舒服
function throttle(fn, delay, isImmediate = true){
    // isImmediate 为 true 时使用前缘节流,首次触发会立即执行,为 false 时使用延迟节流,首次触发不会立即执行
    let last = Date.now();
    return function (){
        let now = Date.now();
        if(isImmediate){
            fn.apply(this, arguments);
            isImmediate = false;
            last = now;
        }
        if(now - last >= delay){
            fn.apply(this, arguments);
            last = now;
        }
    }
}

三、完整使用示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>节流</title>
</head>
<body>
<div>
    <input type="button" value="点我次数加一" id = 'button'>
    <div>
        <span>
            当前点击次数为:
        </span>
        <span id = 'show'>
            0
        </span>
    </div>
</div>

<script>
    /**
     *  节流函数
     */

    // 延迟节流,首次及后续都需要间隔一段时间触发才生效
    function throttleDelayExecution(fn, delay){
        let last = Date.now(); // 使用时间戳实现
        return function (){
            let now = Date.now();
            if(now - last >= delay){
                fn.apply(this, arguments);
                last = now;
            }
        }
    }


    // 前缘节流,首次触发立即生效,后续需要间隔一段时间触发才生效
    function throttleImmediateExecution(fn, delay){
        let timer = null; // 使用定时器实现
        return function (){
            let _this = this; // 当前 this 保存,以免后续处理中 this 丢失
            if(!timer){
                fn.apply(_this, arguments);
                timer = setTimeout(function (){
                    timer = null;
                }, delay);
            }
        }
    }

    // 节流完整版,可选前缘节流或延迟节流,个人感觉这个最舒服
    function throttle(fn, delay, isImmediate = true){
        // isImmediate 为 true 时使用前缘节流,首次触发会立即执行,为 false 时使用延迟节流,首次触发不会立即执行
        let last = Date.now();
        return function (){
            let now = Date.now();
            if(isImmediate){
                fn.apply(this, arguments);
                isImmediate = false;
                last = now;
            }
            if(now - last >= delay){
                fn.apply(this, arguments);
                last = now;
            }
        }
    }


    /**
     * 下面是过程的处理,节点获取,函数封装,注册事件监听器等
     */
    let times = 0;
    let button = document.getElementById('button');
    let show = document.getElementById('show');

    // 实际(最初)的点击事件处理函数,可以在这里进行网络请求之类的操作
    function handleClick(){
        ++times;
        // 这里是替换显示的文本,使用 textContent 比 innerHTML 更安全
        show.textContent = times;
    }

    // 将最初的事件处理函数进行包装,把 handleClick 进行节流处理,返回闭包函数作为新的事件处理函数
    // let packingHandleClick = throttleImmediateExecution(handleClick, 2000);
    // let packingHandleClick = throttleDelayExecution(handleClick, 2000);
    let packingHandleClick = throttle(handleClick, 2000, false);

    // 注册事件监听器
    button.addEventListener("click", packingHandleClick);
</script>
</body>
</html>

参考文章:juejin.cn/post/696695…

想了解防抖的朋友们可以看看我的另一篇文章:防抖函数及其使用示例