资讯专栏INFORMATION COLUMN

函数防抖与函数节流

blankyao / 2340人阅读

摘要:适用情景对象的事件拖拽时的事件射击游戏中的事件文字输入自动完成的事件实际上对于的事件,实际需求大多为停止改变大小毫秒后执行后续处理防抖而其他事件大多的需求是以一定的频率执行后续处理节流。

区别

debounce(防抖):当调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。比如:如果用手指一直按住一个弹簧,它将不会弹起直到你松手为止。

throttle(节流):预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期。比如:将水龙头拧紧直到水是以水滴的形式流出,那你会发现每隔一段时间,就会有一滴水流出。

适用情景

window对象的resize、scroll事件

拖拽时的mousemove事件

射击游戏中的mousedown、keydown事件

文字输入、自动完成的keyup事件

实际上对于window的resize事件,实际需求大多为停止改变大小n毫秒后执行后续处理 (防抖);而其他事件大多的需求是以一定的频率执行后续处理(节流)。

增加一个辅助函数 restArgs

/**
     * 类ES6 rest参数的实现,使某个函数具备支持rest参数的能力
     * @param func 需要rest参数的函数
     * @param startIndex 从哪里开始标识rest参数, 如果不传递, 默认最后一个参数为rest参数
     * @returns {Function} 返回一个具有rest参数的函数
     */
    var restArgs = function (func, startIndex) {
        // rest参数从哪里开始,如果没有,则默认视函数最后一个参数为rest参数
        // 注意, 函数对象的length属性, 揭示了函数的参数个数
        /*
         ex: function add(a,b) {return a+b;}
         console.log(add.length;) // 2
         */r
        startIndex = startIndex == null ? func.length - 1 : +startIndex;
        // 返回一个支持rest参数的函数
        return function () {
            // 校正参数, 以免出现负值情况
            var length = Math.max(arguments.length - startIndex, 0);
            // 为rest参数开辟数组存放
            var rest = Array(length);
            // 假设参数从2个开始: func(a,b,*rest)
            // 调用: func(1,2,3,4,5); 实际的调用是:func.call(this, 1,2, [3,4,5]);
            for (var index = 0; index < length; index++) {
                rest[index] = arguments[index + startIndex];
            }
            // 根据rest参数不同, 分情况调用函数, 需要注意的是, rest参数总是最后一个参数, 否则会有歧义
            switch (startIndex) {
                case 0:
                    // call的参数一个个传
                    return func.call(this, rest);
                case 1:
                    return func.call(this, arguments[0], rest);
                case 2:
                    return func.call(this, arguments[0], arguments[1], rest);
            }
            // 如果不是上面三种情况, 而是更通用的(应该是作者写着写着发现这个switch case可能越写越长, 就用了apply)
            var args = Array(startIndex + 1);
            // 先拿到前面参数
            for (index = 0; index < startIndex; index++) {
                args[index] = arguments[index];
            }
            // 拼接上剩余参数
            args[startIndex] = rest;
            return func.apply(this, args);
        };
    };
debounce

返回 function 函数的防反跳版本, 将延迟函数的执行(真正的执行)在函数最后一次调用时刻的 wait 毫秒之后. 对于必须在一些输入(多是一些用户操作)停止到达之后执行的行为有帮助。 例如: 渲染一个Markdown格式的评论预览, 当窗口停止改变大小之后重新计算布局, 等等.

传参 immediate 为 true, debounce会在 wait 时间间隔的开始调用这个函数 。在类似不小心点了提交按钮两下而提交了两次的情况下很有用。

var debounce = function (func, wait, immediate) {
        var timeout, result;

        var later = function (context, args) {
            timeout = null;
            if (args) result = func.apply(context, args);
        };

        var debounced = restArgs(function (args) {
            // 一旦存在timeout, 意味之前尝试调用过func
            // 由于debounce只认最新的一次调用, 所以之前等待执行的func都会被终止
            if (timeout) clearTimeout(timeout);
            // 如果允许新的调用尝试立即执行,
            if (immediate) {
                // 如果之前尚没有调用尝试,那么此次调用可以立马执行,否则一定得等待之前的执行完毕
                var callNow = !timeout;
                // 刷新timeout
                timeout = setTimeout(later, wait);
                // 如果能被立即执行,立即执行
                if (callNow) result = func.apply(this, args);
            } else {
                // 否则,这次尝试调用会延时wait个时间
                timeout = delay(later, wait, this, args);
            }

            return result;
        });

        debounced.cancel = function () {
            clearTimeout(timeout);
            timeout = null;
        };

        return debounced;
    };
throttle

创建并返回一个像节流阀一样的函数,当重复调用函数的时候,至少每隔 wait毫秒调用一次该函数。对于想控制一些触发频率较高的事件有帮助。

默认情况下,throttle将在你调用的第一时间尽快执行这个function,并且,如果你在wait周期内调用任意次数的函数,都将尽快的被覆盖。如果你想禁用第一次首先执行的话,传递{leading: false},还有如果你想禁用最后一次执行的话,传递{trailing: false}。

var throttle = function (func, wait, options) {

        var timeout, context, args, result;
        // 最近一次func被调用的时间点
        var previous = 0;
        if (!options) options = {};

        // 创建一个延后执行的函数包裹住func的执行过程
        var later = function () {
            // 执行时,刷新最近一次调用时间
            previous = options.leading === false ? 0 : new Date();
            // 清空定时器
            timeout = null;
            result = func.apply(context, args);
            if (!timeout) context = args = null;
        };

        // 返回一个throttled的函数
        var throttled = function () {
            // ----- 节流函数开始执行----
            // 我们尝试调用func时,会首先记录当前时间戳
            var now = new Date();
            // 是否是第一次调用
            if (!previous && options.leading === false) previous = now;
            // func还要等待多久才能被调用 =  预设的最小等待期-(当前时间-上一次调用的时间)
            // 显然,如果第一次调用,且未设置options.leading = false,那么remaing=0,func会被立即执行
            var remaining = wait - (now - previous);
            // 记录之后执行时需要的上下文和参数
            context = this;
            args = arguments;

            // 如果计算后能被立即执行
            if (remaining <= 0 || remaining > wait) {
                // 清除之前的“最新调用”
                if (timeout) {
                    clearTimeout(timeout);
                    timeout = null;
                }
                // 刷新最近一次func调用的时间点
                previous = now;
                // 执行func调用
                result = func.apply(context, args);
                // 如果timeout被清空了,
                if (!timeout) context = args = null;

            } else if (!timeout && options.trailing !== false) {
                // 如果设置了trailing edge,那么暂缓此次调用尝试的执行
                timeout = setTimeout(later, remaining);
            }
            return result;
        };

        // 可以取消函数的节流化
        throttled.cancel = function () {
            clearTimeout(timeout);
            previous = 0;
            timeout = context = args = null;
        };

        return throttled;
    };

点击查看demo

参考文章
http://www.tuicool.com/articl...
http://blog.csdn.net/jinboker...
http://www.css88.com/doc/unde...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/116443.html

相关文章

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

    摘要:文章来源详谈防抖和节流轻松理解函数节流和函数防抖函数防抖和节流好啦,今天的小菊花课堂之的防抖与节流的内容就告一段落啦,感各位能耐心看到这里。 前言 陆游有一首《冬夜读书示子聿》——古人学问无遗力,少壮工夫老始成。纸上得来终觉浅,绝知此事要躬行。,其中的意思想必大家都能明白,在学习或工作中,不断的印证着这首诗的内涵。所以,又有了此篇小菊花文章。 详解 在前端开发中,我们经常会碰到一些会持...

    leoperfect 评论0 收藏0
  • 小菊花课堂之JS的抖与节流

    摘要:文章来源详谈防抖和节流轻松理解函数节流和函数防抖函数防抖和节流好啦,今天的小菊花课堂之的防抖与节流的内容就告一段落啦,感各位能耐心看到这里。 前言 陆游有一首《冬夜读书示子聿》——古人学问无遗力,少壮工夫老始成。纸上得来终觉浅,绝知此事要躬行。,其中的意思想必大家都能明白,在学习或工作中,不断的印证着这首诗的内涵。所以,又有了此篇小菊花文章。 详解 在前端开发中,我们经常会碰到一些会持...

    Yangder 评论0 收藏0
  • JavaScript:函数抖与函数节流

    摘要:函数防抖简单实现模拟请求获取函数的作用域和变量清除定时器节流名词解释连续执行函数,每隔一定时间执行函数。效果函数防抖是某一段时间内只执行一次函数节流是间隔时间执行,不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数。 防抖(debounce) 名词解释:在事件被触发n秒后再执行回调函数,如果在这n秒内又被触发,则重新计时。 使用场景:以百度输入框例,比如你要查询...

    elina 评论0 收藏0
  • 高级函数技巧-函数抖与节流

    摘要:封装方法也比较简单,书中对此问题也进行了处理使用定时器,让函数延迟秒后执行,在此秒内,然后函数再次被调用,则删除上次的定时器,取消上次调用的队列任务,重新设置定时器。 在实际开发中,函数一定是最实用最频繁的一部分,无论是以函数为核心的函数式编程,还是更多人选择的面向对象式的编程,都会有函数的身影,所以对函数进行深入的研究是非常有必要的。 函数节流 比较直白的说,函数节流就是强制规定一...

    whinc 评论0 收藏0
  • 前端进击的巨人(八):浅谈函数抖与节流

    摘要:隆重请出主角防抖与节流。防抖与节流的异同相同都是防止某一时间段内,函数被频繁调用执行,通过时间频率控制,减少回调函数执行次数,来实现相关性能优化。参考文章分钟理解的节流防抖及使用场景函数防抖和节流 showImg(https://segmentfault.com/img/bVburM8?w=800&h=600); 本篇课题,或许早已是烂大街的解读文章。不过春招系列面试下来,不少伙伴们还...

    _Zhao 评论0 收藏0
  • 函数抖与函数节流

    摘要:这可能会导致一些严重的性能问题如果在面试中讨论构建应用程序和事件,如滚动窗口调整大小,或键盘按下的事件时,一定要提及函数防抖动和函数节流来提升页面速度和性能。 前言 有一些浏览器事件可以在很短的事件内快速触发多次,例如 调整窗口大小 或 向下滚动 页面。例如,如果将事件监听器绑定到窗口滚动事件上,并且用户继续非常快速地向下滚动页面,你的事件可能会在3秒的范围内被触发数千次。这可能会导致...

    J4ck_Chan 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<