美文网首页
JavaScript - 节流与去抖

JavaScript - 节流与去抖

作者: wwmin_ | 来源:发表于2018-04-30 11:45 被阅读18次

    1. throttle节流

    简单实现

    /**
    * 频率控制 返回函数连续调用时,action 执行频率限定为 次 / delay
    * @param delay  {number}    延迟时间,单位毫秒
    * @param action {function}  请求关联函数,实际应用需要调用的函数
    * @return {function}    返回客户调用函数
    */
    var throttle = function(delay, action){
      var last = 0;
      return function(){
        var curr = +new Date()
        if (curr - last > delay){
          action.apply(this, arguments)
          last = curr 
        }
      }
    }
    

    setTimeout实现

    function throttle(fn, wait) {
      let _fn = fn,
        timer,
        flags = true;
      return function () {
        let args = arguments,
          self = this;
        console.log(args);
    
        if (flags) {
          _fn.apply(self, args);
          flags = false;
          return flags;
        }
    
        // 如果定时器还在,说明上一次还没执行完,不往下执行
        if (timer) return false;
        timer = setTimeout(function () {//延迟执行
          clearTimeout(timer);//清空上次的定时器
          timer = null;
          _fn.apply(self, args);
        }, wait);
      }
    }
    

    underscore方法的实现

        _.throttle = function (func, wait, options) {
            /* options的默认值
             *  表示首次调用返回值方法时,会马上调用func;否则仅会记录当前时刻,当第二次调用的时间间隔超过wait时,才调用func。
             *  options.leading = true;
             * 表示当调用方法时,未到达wait指定的时间间隔,则启动计时器延迟调用func函数,若后续在既未达到wait指定的时间间隔和func函数又未被调用的情况下调用返回值方法,则被调用请求将被丢弃。
             *  options.trailing = true; 
             * 注意:当options.trailing = false时,效果与上面的简单实现效果相同
             */
            var context, args, result;
            var timeout = null;
            var previous = 0;
            if (!options) options = {};
            var later = function () {
                previous = options.leading === false ? 0 : _.now();
                timeout = null;
                result = func.apply(context, args);
                if (!timeout) context = args = null;
            };
            return function () {
                var now = _.now();
                if (!previous && options.leading === false) previous = now;
                // 计算剩余时间
                var remaining = wait - (now - previous);
                context = this;
                args = arguments;
                // 当到达wait指定的时间间隔,则调用func函数
                // 精彩之处:按理来说remaining <= 0已经足够证明已经到达wait的时间间隔,但这里还考虑到假如客户端修改了系统时间则马上执行func函数。
                if (remaining <= 0 || remaining > wait) {
                    // 由于setTimeout存在最小时间精度问题,因此会存在到达wait的时间间隔,但之前设置的setTimeout操作还没被执行,因此为保险起见,这里先清理setTimeout操作
                    if (timeout) {
                        clearTimeout(timeout);
                        timeout = null;
                    }
                    previous = now;
                    result = func.apply(context, args);
                    if (!timeout) context = args = null;
                } else if (!timeout && options.trailing !== false) {
                    // options.trailing=true时,延时执行func函数
                    timeout = setTimeout(later, remaining);
                }
                return result;
            };
        };
    
    应用
    window.onscroll = throttle(function () {
      console.log("滚动");
    }, 200);
    

    2. debounce去抖

    简单实现

    /**
    * 空闲控制 返回函数连续调用时,空闲时间必须大于或等于 idle,action 才会执行
    * @param idle   {number}    空闲时间,单位毫秒
    * @param action {function}  请求关联函数,实际应用需要调用的函数
    * @return {function}    返回客户调用函数
    */
    var debounce = function(idle, action){
      var last
      return function(){
        var ctx = this, args = arguments
        clearTimeout(last)
        last = setTimeout(function(){
            action.apply(ctx, args)
        }, idle)
      }
    }
    
    //另一种实现
    function debounce(func, wait = 20, immediate = true) {
            let timeout;
            return function () {
                let context = this, args = arguments;
                let later = function () {
                    timeout = null;
                    if (!immediate) func.apply(context, args);
                };
                let callNow = immediate && !timeout;
                clearTimeout(timeout);
                timeout = setTimeout(later, wait);
                if (callNow) func.apply(context, args);
            };
        }
    

    underscore方法的实现

    _.debounce = function(func, wait, immediate) {
        // immediate默认为false
        var timeout, args, context, timestamp, result;
    
        var later = function() {
          // 当wait指定的时间间隔期间多次调用_.debounce返回的函数,则会不断更新timestamp的值,导致last < wait && last >= 0一直为true,从而不断启动新的计时器延时执行func
          var last = _.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 = _.now();
          // 第一次调用该方法时,且immediate为true,则调用func函数
          var callNow = immediate && !timeout;
          // 在wait指定的时间间隔内首次调用该方法,则启动计时器定时调用func函数
          if (!timeout) timeout = setTimeout(later, wait);
          if (callNow) {
            result = func.apply(context, args);
            context = args = null;
          }
    
          return result;
        };
      };
    

    相关文章

      网友评论

          本文标题:JavaScript - 节流与去抖

          本文链接:https://www.haomeiwen.com/subject/xzszattx.html