zoukankan      html  css  js  c++  java
  • 也谈js函数节流

      1、什么是js函数节流

        其本质就是不让某些代码(函数)在没有间断的情况下连续重复执行,目的就是让执行函数的请求停止了一段时间后才执行。

      2、函数节流运用的场景

        窗口大小的改变(resize事件),滚动事件(scroll事件),鼠标移动事件(mousemove事件),touchmove事件(做过手机端的同学一定知道,手机中手指滑动其实触发了多次touchmove),我们在绑定这些事件的时候,函数会被多次执行,因为性能的需要,此时函数节流就派上用场!

      3、函数节流的书写基本形式

        网上也提供很多关于函数节流的模式,根据自身的需要,正常写法有以下几种:

        (1)对象字面量的方式:

    <script type="text/javascript">
         var throttle={
            timeoutId:null,
            performProcessing:function(){
              console.log("节流")
            },
            process:function(){
               clearTimeout(this.timeoutId);    
               var that=this;//为什么要设置这个,亲,setTimeout可是会改变this的指针指向window哦
               this.timeoutId=setTimeout(function(){that.performProcessing()},250)
            }     
         }
         window.onresize=function(){
            throttle.process(); 
         }
      </script>

      当调用process函数的时候,就清除timeoutId来阻止之前的队列函数,然后创建一个新的定时器去调用performProcessing。时间的间隔是250ms,指的是最后一次调用process()之后至少250ms后才会继续调用performProcessing。250ms,代表着在250ms的时间内,不管调用了多少次,performProcessing只会被执行一次(而且是在最后一次被调用的那个函数间隔250ms才添加到队列并执行,如果你一直不间断触发函数,那么performProcessing将永远不会被执行)。

      (2)函数式的方式:

    function throttle(method,context){
               clearTimeout(method.tId);
               method.tId=setTimeout(function(){
                  method.call(context) //改变作用域  
               },250)   
     }
     function resizeFunc(){
             console.log(1)  
     }
     window.onresize=function(){
            throttle(resizeFunc)   
     }

      这种函数节流看来已经不错了,可是发现我们在调用的时候和第1种方式(对象字面量方式)是一样的,这个时候问题就来了。如果页面多次调用,显然这种方式是无法通过参数来改变函数执行的频率(delay),所以难形成共用!怎么办呢?

      那是否有更好的方法呢?如果能把delay的参数放出来呢?所以有了下面的函数写法:

    <script type="text/javascript">
         var throttle = function(fn, delay){
                var timer = null;
                return function(){var context = this, args = arguments;
                    clearTimeout(timer);
                    timer = setTimeout(function(){
                        fn.apply(context, args);
                    }, delay);
                };
         };
         function printed(){
            console.log(1)     
         }
         window.onresize=throttle(printed,250)     
      </script>

      这种方式,脑海中想到的是闭包,没错!就是通过js的闭包的方式,返回函数,通过timer的变量来控制函数的执行!改造完成以后基本能够满足了我们的需求!感觉大功告成了,哈哈!

      可是,发现了没有,如果我不间断地执行某个函数,假设执行了300ms后才停下来,那么意味着setTimeout只有在300ms后才添加到队列,然后执行,那我们所想象的mousemove不就是一闪一闪的结果吗?(因为执行的频率太多频繁而无法立即出发setTimeout),所以我们肯定有必要在规定的时间内,移动要执行某个函数一次,这样就可以必选视觉的误差!所以我们规定了某个函数在视觉允许的范围内,固定时间至少调用一次!

    <script type="text/javascript">
       function throttle(fn,delay,duration){
              var timer=null,last_exec=0;
              return function(){
                 var context=this,args=arguments,elapsed = +new Date();
                 clearTimeout(timer);  
                 if(!last_exec){
                    last_exec = elapsed;
                 }
                 if(elapsed-last_exec > duration){
                      fn.apply(context, args);
                      last_exec=elapsed;     
                 }else {
                        timer = setTimeout(function(){
                            fn.apply(context, args);
                        }, delay);
                 }
              }
          }
         
          function resizeFunc(){
              console.log(1)  
          }
          window.onresize=throttle(resizeFunc,50,5000)
      </script>

       函数的原理很简单,每次执行函数的时候,判断当前的时间elapsed与上一次函数执行的时间的间隔大于delay的时候(elapsed-last_exec)就执行函数fn,然后将last_exec设为当前的日期,这样就可以达到了我们的目的了!

      到这里,认为节流的函数已经结束了饿,可是你看的越多发现自己越无知,有木有呢?因为jquery最近版本已经有了throttle的函数了,把其中的代码如下:

    (function(window,undefined){
      '$:nomunge';
      var $ = window.jQuery || window.Cowboy || ( window.Cowboy = {} ),jq_throttle;
      $.throttle = jq_throttle = function( delay, no_trailing, callback, debounce_mode ) {
        var timeout_id,last_exec = 0;
        if ( typeof no_trailing !== 'boolean' ) {
          debounce_mode = callback;
          callback = no_trailing;
          no_trailing = undefined;
        }
        function wrapper() {
              var that = this,
              elapsed = +new Date() - last_exec,
              args = arguments;
              function exec() {
                last_exec = +new Date();
                callback.apply( that, args );
              };
              function clear() {
                timeout_id = undefined;
              };
              
              if ( debounce_mode && !timeout_id ) {
                exec();
              }
              timeout_id && clearTimeout( timeout_id );
              if ( debounce_mode === undefined && elapsed > delay ) {
                exec();       
              } else if ( no_trailing !== true ) {
                timeout_id = setTimeout( debounce_mode ? clear : exec, debounce_mode === undefined ? delay - elapsed : delay );
              }
        };
        if ( $.guid ) {
          wrapper.guid = callback.guid = callback.guid || $.guid++;
        }
        return wrapper;
      };
      $.debounce = function( delay, at_begin, callback ) {
        return callback === undefined
          ? jq_throttle( delay, at_begin, false )
          : jq_throttle( delay, callback, at_begin !== false );
      };
      
    })(this);

      throttle函数的几个参数的说明:

        delay:延迟的时间(Number)

        no_trailing:默认是false(Boolean),可选参数!如果是false,那么固定每个一段时间(delay)定会调用一次callBack函数,如果是true,那么意味着callBack只会执行最后一次在throttle函数被触发后(听起来晕晕的!请看下图):其实可以理解为:在250ms的时间内,如果函数200ms就执行完了,那么no_trailing=true的时候就不触发callBack,no_trailing=false的时候就促发callBack

      // > Throttled with `no_trailing` specified as false or unspecified:
      // > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
      // > X    X    X    X    X    X        X    X    X    X    X    X
      // > 
      // > Throttled with `no_trailing` specified as true:
      // > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
      // > X    X    X    X    X             X    X    X    X    X

        callBack:就是间隔delay时间要执行的函数

        debounce_mode:可选,不传递任何参数就是undefined!如果是传递的是false的时候,那么就是追后一次执行事件delay的时间后调用callBack(没有固定的时间调用一次),如果debounce_mode=true,事件触发后立刻执行callBack,最后一次事件在delay的时间后触发callBack,开始就触发callBack函数(debounce_mode=false则不是)

      debounce函数的几个参数的说明:

        delay:延迟的时间(Number)

        at_begin:默认false,at_begin=false意味着callBack会在debounce函数最后一次触发函数后执行!

       // > Debounced with `at_begin` specified as false or unspecified:
      // > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
      // >                          X                                 X
      // > 
      // > Debounced with `at_begin` specified as true:
      // > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
      // > X                                 X

        callBack:就是间隔delay时间要执行的函数。

        到这里,函数节流就算结束了,jquery中的封装比较难理解!但是我们只要知道调用方式和参数的作用即可!最后附上参考的网址:

      地址:http://benalman.com/projects/jquery-throttle-debounce-plugin/

    
    

        

  • 相关阅读:
    EcShop二次开发学习方法
    [ 产品经理 ] 互联网产品经理常用软件及工作平台
    Tengine – Nginx衍生版
    把PHP大牛记下来,方便以后关注
    看了极光推送技术原理的几点思考
    centos磁盘满了,查找大文件并清理
    LNMP一键安装包 PHP自动升级脚本
    微信红包系统设计 & 优化
    程序员每天每周每月每年该做的事
    php中$_REQUEST、$_POST、$_GET的区别和联系小结
  • 原文地址:https://www.cnblogs.com/ip128/p/3736036.html
Copyright © 2011-2022 走看看