戏说前端 JavaScript 之『防抖节流』基础知识

往事不堪回首

犹记在很久之前的某次面试中,和那技术大哥谈的正欢呢,冷不防他来了句:谈一谈防抖和节流吧。

抹了把凉汗,我用颤抖的、不自信的语气回答道:呃,防抖就是应该是防止抖动的意思,节流大概是节省流量吧……

当时,我的心理活动是这样的:

坏滴很

当年心理素质是真的差,这个坎没翻过去,最后的结果就是:

悲伤

知耻而后勇

以我不服输的性格,这场子必须找回来!

经过这几年的勤学苦练,我现在自信简直爆棚,如果,时光能够倒流,我相信我可以做到这样:

面对疾风

今天,我要跟「防抖节流」来做个了断,以证明现在的我已不是过去的我,用一个成语来形容,那就是——今非昔比!

知己知彼,方能百战百胜

古老的法则告诉我们:欲要消灭敌人,必须先要了解敌人。

防抖(debounce)

从字面意思来看,防抖就是防止抖动的意思。

举个例子:比如说你对象要你帮她拍美照, 结果因为你加班太多身体虚,手抖的厉害,拍出来的照片模糊不清,为免受雷霆之怒,你就需要想想办法。首先,分析一下照片模糊的原因:

  • 手抖的厉害(1s 十几次!!??)
  • 导致相机不能完美的捕捉画面
  • 所以照片模糊。

看来,「手抖」才是罪魁祸首啊,那想个办法解决手抖的问题就好了:找一个支架,让它代替手来支撑相机,这样就不「抖」了!

这操作,让我不得不对你竖起大拇指:

想想看上面这个例子本质上是解决了什么问题?

我觉得应该是解决了「抖动频率」的问题——手拿着,一秒会抖好多次;可支架撑着,好长时间都未必会抖一次。可谓是「人不如架」啊!

支架起的就是防抖的作用。

回到我们今天的正题上,来看一下防抖的常规解释:当函数被连续调用时,该函数并不执行,只有当其全部停止调用超过一定时间后才执行1次。

防抖

节流(throttle)

现在回头看看我当年的回答,还是摸到了一点门槛(手动缓解尴尬,嘿嘿)。

再来个例子:家里的水龙头,即使你开的再大,它在单位时间里能流出的水就那么多,因为水龙头的规格限制了它单位时间里的流量。

水龙头起到的就是节流的作用。

节流的常规解释:规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效

节流

常用场景

防抖节流一般用于以下场景:

  • 浏览器窗口的缩放,页面的滑动触发的 resizescroll
  • 鼠标事件 onmousemoveonmousedownonmouseup
  • 输入框录入触发的 keyupkeydown……

原理

防抖是维护一个计时器,规定在 delay 时间后触发函数,但是在 delay 时间内再次触发的话,都会清除当前的 timer 然后重新设置超时调用,即重新计时。这样一来,只有最后一次操作能被触发。

节流是通过判断是否到达一定时间来触发函数,若没到规定时间则使用计时器延后,而下一次事件则会重新设定计时器。

区别

函数防抖是某一段时间内只执行一次,而函数节流是间隔时间执行,不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数。

总结

今天就先来了解一下「防抖」和「节流」的基础知识,详细代码实现待后续。

 

 

出处:https://www.cnblogs.com/bianchengsanmei/p/14891070.html

=======================================================================================

简单代码实现

前戏

首先,总结一下上一篇文章——《前端 JavaScript 之『防抖』的简单代码实现》的内容:「防抖」就是在高频率触发事件停止触发后,延时执行某个处理逻辑。

防抖虽然在一定程度上对性能起到了优化效果,但是,我们也要看到它的局限性:如果高频率事件一直触发,那么回调函数中的逻辑就一直得不到执行。

大家都知道,掘金的编辑器带有草稿箱的效果,即你输入的内容会保存下来,即使我们退出编辑器页面了,再次进入还是可以找回前面输入的内容,这是因为编辑器的 input 事件中执行了发送内容的防抖函数。如下图所示:

现在假设:你的打字速度很快,基本上不带喘口气的,那么是否会导致很长时间都保存不了一次数据?如果这时候你再以迅雷不及掩耳之势关闭掉浏览器,是不是你所写的内容就保存不下来了?

那么我们是否可以想办法避免上面这种情况呢?

在这种需求背景下,我们今天的主人公——「节流」开始粉墨登场。

节流的含义大家应该都知道了:每隔固定的时间都会执行一次回函函数中的逻辑

不使用节流函数,我们来看一下下面这个功能的执行效果:

<body>
    <form action="" class="example-form">
        <div>
            <label for="name">
                名称
            </label>
            <input class="input-ele" type="text" name="name" id="name" placeholder="please input your name"
                autocomplete="off">
        </div>
        <div>
            <label for="res">
                输入
            </label>
            <textarea class="input-ele" type="multipart" name="res" id="res" readonly
                placeholder="这里是每一次输入的结果"></textarea>
        </div>
    </form>
</body>

<script>
    window.onload = () => {
        const inputEle = document.querySelector("#name");
        const resEle = document.querySelector("#res");
        inputEle.addEventListener("input", function (event) {
            console.log(this.value);
            resEle.value += `\n${ this.value }`
        });
    }
</script>

实现的效果如下:

即使我们想要执行输出逻辑,但是也不能接受这么高频率的输出,一个是造成了输出内容冗余,二是渲染强度高,不太划算。

新需求

假如,现在有这么一个新需求,要我们在 input 事件中加入新的逻辑:每隔一段时间后,都会执行一次回调函数中的逻辑。

这个需求是不是挺符合节流函数的使用场景的,那我们赶紧来实现一个吧。

实现节流

根据节流函数的定义:以固定的低频率执行代码逻辑,具体到我们上面的额需求来说,只要打开页面,不管你有没有输入,都会每隔几秒就执行一次保存数据的逻辑。

window.onload = function () {
    const resEle = document.querySelector("#res");
    function changeOutputVal(value) {
        resEle.value += `\n${ value }`;
    }
    function throttle(fun, delay) {
        let last, deferTimer
        return function (args) {
            let that = this;
            let _args = arguments;

            let now = +new Date();
            if (last && now < last + delay) {
                clearTimeout(deferTimer);
                deferTimer = setTimeout(function () {
                    last = now;
                    fun.apply(that, _args);
                }, delay)
            } else {
                last = now;
                fun.apply(that, _args);
            }
        }
    }
    const outputRes = throttle(changeOutputVal, 2000);

    const inputEle = document.querySelector("#name");

    inputEle.addEventListener("input", (eve) => {
        outputRes(eve.target.value);
    });
}

代码说明:

  • 每一次事件被触发,都会判断间隔时间是否大于等于 delay,如果是,则执行输出逻辑;如果否,则清除原先的延时器,重新计算延时时间;

运行效果如下:

可以看到,在加入节流代码之后,输出事件不会每次 input 事件都触发,而是每隔 delay 时间触发一次。

代码比较粗糙,也比较基础,后面会逐步向着复杂的方向迭代,望各位看官海涵🙏

 

 

出处:https://www.cnblogs.com/bianchengsanmei/p/14903382.html

posted on 2021-06-21 09:54  jack_Meng  阅读(113)  评论(0编辑  收藏  举报

导航