函数节流与防抖

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

个人博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

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