函数节流与防抖

2019-12-23  本文已影响0人  写前端的大叔

函数节流与防抖在操作dom的时候很实用。比如用户在点击提交按钮时,防止用户重复点击导致多次提交一样的数据,为了限制用户重复点击,我们需要采取措施来防止重复提交数据,这就可以用到我们所说的函数防抖,在一定时间类触发同样的事件时,只执行最后一次。最近正好在看underscore.js的源码,看到了节流和防撞的实现,今天抽时间来整理下函数的节流与防抖在实际开发中的应用场景。

1.节流

节流是指函数按照固定的时间来执行一次,比如调用鼠标事件的mousemove时,如果没做任何处理,当鼠标移动的时候,会一直触发mousemove的回调函数,假设使用函数节流,将固定时间设置为200毫秒一次,在这200毫秒内,无论鼠标怎么移动,都不会触发`mousemove``的回调函数,这就是我们所说的节流。

1.1应用场景

主要是应用在一些高频事件,比如mousemovekeyup等。

1.2实现原理

节流的实现原理主要是通过定时器来完成,设定一个时间后,判断定时器是否为空,如果定时器为空就立即执行,定时器不为空时,在定时器设置的时间内不执行回调函数,达到设置的时间后才执行回调函数,具体的实现代码如下所示,代码使用的是underscore.js中节流的代码。

        /*
        * 节流
        * */
        this.throttle = function (func,wait) {
            var timeout, context, args, result;
            var previous = 0;
            var self = this;
            var later = function() {
                previous = self.now();
                timeout = null;
                result = func.apply(context, args);
                if (!timeout) context = args = null;
            };

            var throttled = function() {
                var now = self.now();
                if (!previous) previous = now;
                var remaining = wait - (now - previous);
                context = this;
                args = arguments;
                if (remaining <= 0 || remaining > wait) {
                    if (timeout) {
                        clearTimeout(timeout);
                        timeout = null;
                    }
                    previous = now;
                    result = func.apply(context, args);
                    if (!timeout) context = args = null;
                } else if (!timeout) {
                    timeout = setTimeout(later, remaining);
                }
                return result;
            };

            throttled.cancel = function() {
                clearTimeout(timeout);
                previous = 0;
                timeout = context = args = null;
            };

            return throttled;
        }

2.防抖

当持续触发事件时,在一定时间内没有再次触发时才会执行一次,如果在指定时间内持续触发,每次触发的时候将重新计时。比如给按钮添加点击事件时,用户快速的连续点击按钮,只执行规定时间内最后执行的那一次。

2.1应用场景

主要是应用在一些持续触发事件,比如快速点击,scroll,resize等事件。

2.2实现原理

防抖的实现原理还是借助于定时器,当持续触发函数时,每次将定时器清空重置,重新生成一个定时器,然后再指定的时间后执行回调函数,具体的实现代码如下所示,代码使用的是underscore.js中节流的代码。

      /*
        * 去抖
        * */
        this.debounce = function (func,wait,immediate) {
            var timeout, args, context, timestamp, result,self = this;
            var later = function() {
                var last = self.now() - timestamp;
                if (last < wait && last >= 0) {
                    timeout = setTimeout(later, wait - last);
                } else {
                    timeout = null;
                    if (!immediate) {
                        result = func.apply(context, args);
                        if (!timeout)
                            context = args = null;
                    }
                }
            };

            return function() {
                context = this;
                args = arguments;
                timestamp = self.now();
                var callNow = immediate && !timeout;
                if (!timeout)
                    timeout = setTimeout(later, wait);

                if (callNow) {
                    result = func.apply(context, args);
                    context = args = null;
                }

                return result;
            };
        }

实例

针对mousemove事件和click事件写了一个简单的demo,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>节流去抖</title>
    <style>
        #red-layout{
            width: 100px;
            height: 100px;
            position: absolute;
            background-color: red;
        }
    </style>
</head>
<body onload="init()">
    <div id="red-layout"></div>
</body>
<script>
    function init() {
        var util = new Util();
        var layout = document.getElementById('red-layout');
        layout.addEventListener("mousemove", util.throttle(function (event) {
            console.log('mousemove');
        },500));
        layout.addEventListener('click',util.debounce(function () {
            console.log('click');
        },2000))
    }

    function Util() {
        /*
        * 节流
        * */
        this.throttle = function (func,wait) {
            var timeout, context, args, result;
            var previous = 0;
            var self = this;
            var later = function() {
                previous = self.now();
                timeout = null;
                result = func.apply(context, args);
                if (!timeout) context = args = null;
            };

            var throttled = function() {
                var now = self.now();
                if (!previous) previous = now;
                var remaining = wait - (now - previous);
                context = this;
                args = arguments;
                if (remaining <= 0 || remaining > wait) {
                    if (timeout) {
                        clearTimeout(timeout);
                        timeout = null;
                    }
                    previous = now;
                    result = func.apply(context, args);
                    if (!timeout) context = args = null;
                } else if (!timeout) {
                    timeout = setTimeout(later, remaining);
                }
                return result;
            };

            throttled.cancel = function() {
                clearTimeout(timeout);
                previous = 0;
                timeout = context = args = null;
            };

            return throttled;
        }

        /*
        * 去抖
        * */
        this.debounce = function (func,wait,immediate) {
            var timeout, args, context, timestamp, result,self = this;
            var later = function() {
                var last = self.now() - timestamp;
                if (last < wait && last >= 0) {
                    timeout = setTimeout(later, wait - last);
                } else {
                    timeout = null;
                    if (!immediate) {
                        result = func.apply(context, args);
                        if (!timeout)
                            context = args = null;
                    }
                }
            };

            return function() {
                context = this;
                args = arguments;
                timestamp = self.now();
                var callNow = immediate && !timeout;
                if (!timeout)
                    timeout = setTimeout(later, wait);

                if (callNow) {
                    result = func.apply(context, args);
                    context = args = null;
                }

                return result;
            };
        }

        /*
        * 获取当前时间
        * */
        this.now =  Date.now || function() {
            return new Date().getTime();
        }
    }
</script>
</html>

个人博客

上一篇 下一篇

猜你喜欢

热点阅读