小菊花课堂之JS的防抖与节流

前言

陆游有一首《冬夜读书示子聿》——“古人学问无遗力,少壮工夫老始成。纸上得来终觉浅,绝知此事要躬行。”,其中的意思想必大家都能明白,在学习或工作中,不断的印证着这首诗的内涵。所以,又有了此篇小菊花文章。

详解

在前端开发中,我们经常会碰到一些会持续触发的时间,比如 输入框校验、resize、scroll、mousemove 等操作时,如果事件触发的频率无限制,会家中浏览器的负担,导致用户体验非常糟糕。
我们可以先看看持续触发过程中频繁执行函数是怎样的情况

<div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
<script>
    var num = 1;
    var content = document.getElementById('content');

    function count() {
        content.innerHTML = num++;
    };

    content.onmousemove = count;
</script>

小菊花课堂之JS的防抖与节流
在上面代码中,div 元素绑定了 mousemove 事件,当鼠标在 div(灰色)区域中移动的时候会持续地去触发该事件导致频繁执行函数。

再看一个例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>没有防抖</title>
    <style type="text/css"></style>
    <script type="text/javascript">
        window.onload = function () {
            //模拟ajax请求
            function ajax(content) {
                console.log('ajax request ' + content)
            }
            let inputNormal = document.getElementById('normal');

            inputNormal.addEventListener('keyup', function (e) {
                ajax(e.target.value)
            })
        }
    </script>
</head>
<body>
    <div>
        1.没有防抖的input:
        <input type="text" name="normal" id="normal">
    </div>
</body>
</html>

小菊花课堂之JS的防抖与节流

在上面代码中,会监听键盘输入事件,只要按下键盘,就会触发这次模拟的ajax请求,不仅浪费了资源,而且在实际应用中,用户也是需要输入完整字符后,才请求。

防抖(debounce)

简单来说就是防止抖动,指触发事件在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
我们将上面的代码加入防抖优化一下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>加入防抖</title>
    <style type="text/css"></style>
    <script type="text/javascript">
        window.onload = function () {
            //模拟ajax请求
            function ajax(content) {
                console.log('ajax request ' + content)
            }
            function debounce(fun, delay) {
                return function (args) {
                    //获取函数的作用域和变量
                    let that = this
                    let _args = args
                    //每次事件被触发,都会清除当前的timeer,然后重写设置超时调用
                    clearTimeout(fun.id)
                    fun.id = setTimeout(function () {
                        fun.call(that, _args)
                    }, delay)
                }
            }
            let inputDebounce = document.getElementById('debounce')
            let debounceAjax = debounce(ajax, 500)
            inputDebounce.addEventListener('keyup', function (e) {
                debounceAjax(e.target.value)
            })
        }
    </script>
</head>
<body>
    <div>
        2.加入防抖后的输入:
        <input type="text" name="debounce" id="debounce">
    </div>
</body>
</html>

小菊花课堂之JS的防抖与节流

上面代码加入防抖后,当持续在输入框里输入时,并不会发送请求,只有当在指定时间间隔内没有再输入时,才会发送请求。如果先停止输入,但是在指定间隔内又输入,会重新触发计时。

节流(throttle)

规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
我们同样在上面的需求上进行修改,加入节流函数。

//模拟ajax请求
function ajax(content) {
    console.log('ajax request ' + content)
}

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);
        }
    }
}
let throttleAjax = throttle(ajax, 1000)
let inputThrottle = document.getElementById('throttle')
inputThrottle.addEventListener('keyup', function (e) {
    throttleAjax(e.target.value)
})

小菊花课堂之JS的防抖与节流

从上面代码可以看出,规定每一秒执行一次ajax请求,效果图也能比较清晰的反应出来。

小结

区别

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

在其他同学的文章中看到这样的解释:
防抖 — 如果有人进电梯(触发事件),那电梯将在10秒钟后出发(执行事件监听器),这时如果又有人进电梯了(在10秒内再次触发该事件),我们又得等10秒再出发(重新计时)。

节流 — 我们知道目前的一种说法是当 1 秒内连续播放 24 张以上的图片时,在人眼的视觉中就会形成一个连贯的动画,所以在电影的播放(以前是,现在不知道)中基本是以每秒 24 张的速度播放的,为什么不 100 张或更多是因为 24 张就可以满足人类视觉需求的时候,100 张就会显得很浪费资源。

这大概可以较为清晰的讲出这两者的区别吧。

原理

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

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

文章来源
1.详谈js防抖和节流
2.轻松理解JS函数节流和函数防抖
3.函数防抖和节流

好啦,今天的小菊花课堂之JS的防抖与节流的内容就告一段落啦,感各位能耐心看到这里。
see u ~ again

相关推荐