小陈同学の前端笔记 | 来简单了解下函数节流

236 阅读3分钟

「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战

昨天我们简单介绍了函数防抖的概念以及如何手写,详情请看 小陈同学の前端笔记 | 来简单了解下函数防抖 这篇文章。今天我们来看一下限制事件频繁触发的另一种解决方案:节流

什么是节流

所谓节流,就是在频繁触发事件时,每隔一段时间,执行一次函数。

* 节流会稀释函数的执行频率。

以上篇文章中的鼠标移动为例,假设我们规定的时间为 1s ,如果我们频繁地不间断地触发鼠标移动事件,那么每隔 1s 才会执行一次函数。

节流的实现

根据实现的方式划分,目前主流的有两种方式:

  1. 时间戳版
  2. 定时器版

时间戳版

使用时间戳,即在触发事件的时候,记录下当前的时间戳,与之前所记录的时间戳做减法,若大于我们人为设置的时间间隔,则执行函数,并更新最新的时间戳;若小于,则不执行函数。

代码如下:

function addNum(){
    box.innerHTML = num++
}

function throttleTimeStamp(func,delay) {
    let previous = 0 //初始时间戳置为0

    return function(){
        let now = new Date()
        if(now - previous > delay){
            func()
            previous = now
        }
    }
}

// 调用
box.onmousemove = throttleTimeStamp(addNum,2000)

dcq4h-gkp0n.gif

我们可以看到当鼠标移入的时候,函数会 立即执行,但在 2s 内只执行一次,2s 后才会执行第二次,这就起到了稀释函数执行的作用。

定时器版

使用定时器,即在触发事件的时候,设置一个定时器,再触发事件时,如果定时器存在,就不执行函数。等到定时器时间到了,再执行函数,清空当下的定时器,并设置下一个定时器。

代码如下:

function throttleTimer(func,delay) {
    let timer;

    return function(){
        if(!timer){
            timer = setTimeout(() => {
                timer = null
                func()
            }, delay);
        }
    }
}

box.onmousemove = throttleTimer(addNum,2000)

1.gif

这是使用定时器方式的实现效果,我们可以发现,当鼠标移入的时候,函数 不会立即执行,而是等待 2s 后才会执行,且当我们把鼠标移出去的时候,最后一次还是会执行(因为定时器里的函数在离开前并没有调用,等待时间后完成最后一次。)

最终版本

该版本结合了时间戳版的 立即执行与定时器版的 停止触发后再执行一次,并修改了 this 的指向以及解决了 参数的传递 问题。

代码如下:

function throttle(func, wait) {
    let timeout, context, args, result;
    let previous = 0;

    let later = function() {
        previous = new Date();
        timeout = null;
        func.apply(context, args)
    };

    let throttled = function() {
        let now = new Date();
        //下次触发 func 剩余的时间
        let remaining = wait - (now - previous);
        context = this;
        args = arguments;
        // 如果没有剩余的时间或改了系统时间
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            func.apply(context, args);
        } else if (!timeout) {
            timeout = setTimeout(later, remaining);
        }
    };
    return throttled;
}

2.gif

可以发现,不仅仅实现了 立即执行函数,而且也做到了停止触发事件后再执行一次函数

结语

以上就是节流函数的介绍以及实现的代码,如有纰漏,欢迎指出!

参考文献: github.com/mqyqingfeng…