函数防抖和节流

在前端开发的过程中,我们经常会需要绑定一些持续触发的事件,如 resize、scroll、mousemove 等等,但有些时候我们并不希望在事件持续触发的过程中那么频繁地去执行函数。

防抖(debounce):所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

节流(throttle):所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。

防抖是将多次执行变为最后一次执行,节流是将多次执行变为每隔一段时间执行。

一、未做防抖节流

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>未做防抖节流</title>
    </head>
    <body>
        <div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
        <script>
            let num = 1;
            let content = document.getElementById('content');

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

            content.onmousemove = count;
        </script>
    </body>
</html>

二、防抖(debounce)

当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定时间到来之前,又触发了事件,就重新开始延时。也就是说当一个用户一直触发这个函数,且每次触发函数的间隔小于既定时间,那么防抖的情况下只会执行一次。

1.先防抖再执行函数

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>先防抖再执行</title>
    </head>
    <body>
        <div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
        <script>
            let num = 1;
            let content = document.getElementById('content');

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

            function debounce(func, wait) {
                let timeout;
                return function() {
                    let context = this;
                    let args = arguments;

                    if (timeout) clearTimeout(timeout);

                    timeout = setTimeout(() => {
                        func.apply(context, args)
                    }, wait);
                }
            }

            content.onmousemove = debounce(count, 1000);
        </script>
    </body>
</html>

2.先执行函数再防抖

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>先执行再防抖</title>
    </head>
    <body>
        <div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
        <script>
            let num = 1;
            let content = document.getElementById('content');

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

            function debounce(func, wait) {
                let timeout;
                return function() {
                    let context = this;
                    let args = arguments;

                    if (timeout) clearTimeout(timeout);

                    let callNow = !timeout;
                    timeout = setTimeout(() => {
                        timeout = null;
                    }, wait)

                    if (callNow) func.apply(context, args)
                }
            }

            content.onmousemove = debounce(count, 1000);
        </script>
    </body>
</html>

3.防抖结合

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>防抖结合</title>
    </head>
    <body>
        <div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
        <script>
            let num = 1;
            let content = document.getElementById('content');

            function count() {
                console.log(num);
                content.innerHTML = num++;
            };
            
            /**
             * @desc  函数防抖
             * @param func 函数
             * @param wait 延迟执行毫秒数
             * @param immediate true 表立即执行,false 表非立即执行
             */
            function debounce(func, wait, immediate) {
                let timeout;

                return function() {
                    let context = this;
                    let args = arguments;

                    if (timeout) clearTimeout(timeout);
                    if (immediate) {
                        var callNow = !timeout;
                        timeout = setTimeout(() => {
                            timeout = null;
                        }, wait)
                        if (callNow) func.apply(context, args)
                    } else {
                        timeout = setTimeout(function() {
                            func.apply(context, args)
                        }, wait);
                    }
                }
            }

            content.onmousemove = debounce(count, 1000, false);
        </script>
    </body>
</html>

三、节流(throttle)

当持续触发事件时,保证在一定时间内只调用一次事件处理函数,意思就是说,假设一个用户一直触发这个函数,且每次触发小于既定值,函数节流会每隔这个时间调用一次。

1.节流时间戳版

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>节流时间戳版</title>
    </head>
    <body>
        <div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
        <script>
            let num = 1;
            let content = document.getElementById('content');

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

            function throttle(func, wait) {
                let previous = 0;
                return function() {
                    let now = Date.now();
                    let context = this;
                    let args = arguments;
                    if (now - previous > wait) {
                        func.apply(context, args);
                        previous = now;
                    }
                }
            }

            content.onmousemove = throttle(count, 1000);
        </script>
    </body>
</html>

2.节流定时器版

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>节流定时器版</title>
    </head>
    <body>
        <div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
        <script>
            let num = 1;
            let content = document.getElementById('content');

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

            function throttle(func, wait) {
                let timeout;
                return function() {
                    let context = this;
                    let args = arguments;
                    if (!timeout) {
                        timeout = setTimeout(() => {
                            timeout = null;
                            func.apply(context, args)
                        }, wait)
                    }
                }
            }

            content.onmousemove = throttle(count, 1000);
        </script>
    </body>
</html>

3.节流结合版

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>节流结合版</title>
    </head>
    <body>
        <div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
        <script>
            let num = 1;
            let content = document.getElementById('content');

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

            /**
             * @desc  函数节流
             * @param func 函数
             * @param wait 延迟执行毫秒数
             * @param type 1 表时间戳版,2 表定时器版
             */
            function throttle(func, wait, type) {
                if (type === 1) {
                    console.log("1:");
                    var previous = 0;
                } else if (type === 2) {
                    console.log("2:");
                    var timeout;
                }
                return function() {
                    let context = this;
                    let args = arguments;
                    if (type === 1) {
                        let now = Date.now();

                        if (now - previous > wait) {
                            func.apply(context, args);
                            previous = now;
                        }
                    } else if (type === 2) {
                        if (!timeout) {
                            timeout = setTimeout(() => {
                                timeout = null;
                                func.apply(context, args)
                            }, wait)
                        }
                    }
                }
            }

            content.onmousemove = throttle(count, 1000, 2);
        </script>
    </body>
</html>

四、总结

防抖是控制次数,节流是控制频率。

防抖是将多次执行变为最后一次执行,节流是将多次执行变为每隔一段时间执行。

五、参考文章

https://www.jianshu.com/p/c8b86b09daf0

一辈子很短,努力的做好两件事就好;第一件事是热爱生活,好好的去爱身边的人;第二件事是努力学习,在工作中取得不一样的成绩,实现自己的价值,而不是仅仅为了赚钱。
原文地址:https://www.cnblogs.com/antao/p/13248359.html