函数节流与防抖

函数节流与防抖在操作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>

个人博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 最近频繁用到浏览器窗口的resize事件和页面的滚动事件(scroll),我们都知道这些事件会频繁的调用回调函数,...
    snow_in阅读 3,446评论 0 0
  • 函数节流(throttle)与 函数防抖(debounce)都是为了限制函数的执行频次,以优化函数触发频率过高导致...
    _Dot912阅读 4,189评论 0 6
  • 标签:debounce throttle iplas 小编在偶然浏览Javascript技术贴吧时候,偶然看到节...
    傑仔阅读 3,493评论 0 2
  • 函数节流(throttle)当持续触发事件时,保证一定时间段内只调用一次事件处理函数当触发事件的时候,我们设置一个...
    梦游2阅读 1,229评论 0 0
  • 事件频繁触发带来的问题 一些浏览器的事件,触发的频率非常高,高触发的同时,不仅浪费了浏览器的资源,还会影响浏览器的...
    平平凡凡一头猪阅读 1,886评论 0 0

友情链接更多精彩内容