zoukankan      html  css  js  c++  java
  • js函数防抖与函数节流

    差别

    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 时间间隔的開始调用这个函数 。

    在类似不小心点了提交button两下而提交了两次的情况下非常实用。

    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毫秒调用一次该函数。

    对于想控制一些触发频率较高的事件有帮助。

    (愚人码头注:详见:javascript函数的throttle和debounce,感谢 @澳利澳先生 的翻译建议)

    默认情况下,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;
        };

  • 相关阅读:
    GoLang中面向对象的三大特性
    Go常用功能总结一阶段
    GO语言基础之并发concurrency
    GO语言基础之error
    GO语言基础之reflect反射
    GO语言基础之interface
    GO语言基础之method
    GO语言基础之struct
    GO语言基础map与函数
    GO语言基础条件、跳转、Array和Slice
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/7367965.html
Copyright © 2011-2022 走看看