首页 > 代码库 > jQuery源码解读第5章---对Callbacks的解读

jQuery源码解读第5章---对Callbacks的解读

jQuery.Callbacks() 是一个多用途的回调函数列表对象

提供了一种强大的方法来管理回调函数队列

先来看看Callbacks的常见的用法

1-------不带参数

先看看不用回调函数的例子

eq

function a1(){

  console.log(‘a1‘)

}

(function(){

  function a2(){

    console.log(‘a2‘)

  }

})()

a1() // a1

a2() //就不行了

这时候我们就可以使用回调函数Callbacks 了

 

var dfd1 = $.Callbacks();

function a1(){

  console.log(‘a1‘)

}

dfd1.add(a1)

(function(){

  function a2(){

    console.log(‘a2‘)

  }

  dfd1.add(a2)

})()

dfd1.fire() // a1 a2

Callbacks提供了一个强大的方法来管理回调函数列表

 

上面已经说过无参数的情况了

下面来说一说有参数的情况,,,,

技术分享

 

jQuery的源码,我们可以看到Callbacks回调函数有参数的情况分为4种  分别为once memory unique stopOnFalse 这四种情况

下文我用例子分别解释一下

1------ once --- 确保这个回调列表只执行一次 (通俗的说fire()这个函数只能执行一次)

eq

var dfd2 = $.Callbacks(‘once‘);

function a1(){

  console.log(‘a1‘)  

}

dfd2.add(a1)

dfd2.fire();

dfd2.fire();

这样只能执行一个fire()

r如果没有options 为once的话  是会执行两个fire()

 

 2--------unique ---确保一次只能添加一个回调( 也就是只能不能添加相同的函数)

once 和 unique 比较相似,,,,所以我先说

eq

var dfd3 = $.Callbacks(‘unique‘);

function a1(){

  console.log(‘a1‘)

}

dfd3.add(a1)

dfd3.add(a1)

dfd3.fire(); //a1

这时候只有一个函数执行而已  ..

3-------- memory --- 就是保持到最后一个add()执行完再执行 ( 其实就是不管fire()在什么地方,,,都会执行完所有回调函数 )

eq

var dfd4 = $.Callbacks(‘memory‘);

function a1(){

  console.log(‘a1‘)

}

dfd4.add(a1)

dfd4.fire();// 如果没有options 为memory的话,,,,后面的add 就不会执行了

(function(){
function a2(){
console.log(‘a2‘)
}
dfd2.add(a2)
})()
这时候所有回调函数都会执行

4----- stopOnFalse ----当一个回调函数返回false时候就中断回调
var dfd5 = $.Callbacks(‘stopOnFalse‘);
function a1(){
  console.log(‘a1‘);
  return false;
}
dfd5.add(a1)
(funciton(){
  function a2(){
    console.log(‘a2‘)
  }
  dfd5.add(a2)
})()

dfd5.fire() //如果没有stopOnFalse 和return false 的话,,,,,,这时候 结果应该为 a1 a2 的
但是中断了回调了 ,,,,只出现a1而已
基本的使用情况已经说完,,,,,下面开始解读源码了

当然回调对象Callbacks 还有一些具体的方法 如callbacks.add() callbacks.fire() callbacks.remove()等等
这些我到看到再说,,,,

先看看总体结构,

jQuery.Callbacks = function(  options ){
  var 
    memory,
    firing,
    ......
    self = {
      add: function(){
      
      },
      remove : function(){

      }
      .......
    }
  return self;
  
}

 

 
//这里就是对options配置参数处理的函数
var optionsCache = {};

我们要明白一点就是 options 配置参数可以是 ‘ once memory ‘ 或者‘once‘这样的
function createOptions( options ){
  var object = optionsCache[ options ] ={};

  这里我们要用到jQuery.each()这个方法
  这个方法的源码,,,,我们还没有学,,,,但是jQuery.each这个工具方法,,是我们平时常用的,,,,我们对这个方法应该很熟悉才对,,,
  所以不影响我们的学习

  正则match()方法找到符合的,则放到数组,,,,, 如果没有的话,,,就返回null
  上面定义好了 core_rnotwhite = /\S+/g ------ >非空字符

  jQuery.each( options.match( core_rnotwhite ) || [],function(_,flag){
    object[ flag ] = true;
  } )

  
  
  return object;


这就是总体结构

jQuery.Callbacks = function( options ){
  //对options的处理和判断
  options = typeof options === ‘string‘ ?
    ( optionsCache[ options ] || createOptions(options) ) :
    
    先判断options是否有缓存,,,,有的话,,就不执行createOptions(options)这个函数

    jQuery.extend( {}, options ); //这个一般指的是options为空的时候,,,,,如果options是数组或者别的,,,,是无效的
   
    //定义一系列变量 
    var
      memory,firing,fired,firingIndex,firingStart,firingLength, 
      stack = !options.once && [], //once 存在 ,,stack 为false once不存在,,,,stack 为[]
      list =[ ], //定义一个数组用来存放函数

      /*******PS 这部分本人建议先看完后面的再回来看 可以先跳过*********************************/
     
 1  fire = function(data){
 2         memory = options.memory && data; //如果存在memory这个配置参数,,,,,,变量memory就为data
 3 
 4         fired = true; //标记已经触发过了
 5         
 6         firingIndex = firingStart || 0;
 7 
 8         firingStart = 0;
 9     
10         firingLength = list.length;
11 
12         firing = true;    //标记正在触发回调函数列表
13         
14         for( ;firingIndex < firingLength;firingIndex++ ){
15           if(  list[firingIndex].apply(data[0],data[1])  === false && options.stopOnFalse ){
16               memory = false;
17               break; //退出循环
18           }
19         }
20 
21         firing = false;
22 
23         if( list ){
24           if(stack){
25             if( stack.length ){
26               fire(stack.shift())
27             }
28           }else if(memory){
29             list = []
30           }else{
31             self.disable();
32           }
33         }
34   
35         
36         
37       }

 


      

      这个fire() 是最终触发回调函数列表的

      重点解释
      1------> list[ firingIndex ].apply(data[0],data[1])
        这段代码即是执行回调函数的
        把list里面的每个函数,,,,,放到data[0](即回调对象中)执行 参数为data[1]
        
        同时这里对配置参数stopOnFalse 做了判断
        当执行里面的回调函数 ....... 有return false 而且 存在stopOnFalse 这个参数的话...
        执行memory = false ;-----> 阻止未来add()中产生的回调
  
        技术分享

      2--------> fire(stack.shift())
        当stack.length 不为0 时....说明堆栈有参数
       stack.shift() --- 通过使用数组的shift() .....把第一个元素从数组中删除,,,并返回第一个元素
       就递归执行fire() 这个函数
       
   

 



      
      
       /****************************************/


      self = {
        add : function(){
          /*

           add方法是回调函数中常用的方法
          上面的例子中到用到
          */
          if(list){ //list 就算是空数组,,也是true
            var start = list.length;
            
            
            //这里用了一个立即执行的add函数来添加回调
            (function add(args){
              jQuery.each(args,function(_,arg){
                var type = jQuery.type(arg);
                if( type === ‘function‘ ){
                  if( !options.unique || !self.has(arg)){
                    /*
                      !options.unique || !self.has(arg)的意思就是
                      没有参数unique的话 .......> 直接添加,,,不用判断!self.has(arg)
                      如果有参数uniqued------> 说明不能添加一样的,,就要判断self.has(arg)
                      self.has(arg) //用于判断回调列表中是否包含arg
                       通过判断是否有unique 和判断在回调列表中是否存在 就可以知道arg 要不要添加到list中了
  
 
                    */
                    list.push(arg)
                    
                  }else if( arg && arg.length && type !== ‘string‘){  
                     /*
                      arg && arg.length && type !== ‘string‘这个判断语句
                      通过判断type !== ‘string‘  和arg.length 有length这个属性  从而得出是一个数组
                      eq
                        var cb = $.Callbacks();
                        function a1(){
                          console.log(‘a1‘)
                        }
                        function a2(){
                          console.log(‘a2‘)
                        }
                        cb.add([a1,a2])
                        cb.fire();// a1  a2
                      既然是数组,,,,那只能通过递归来添加回调了
                        

                     */
                     add(arg)
                  }
  
                }
              });
            })(arguments)

 


            
            
          }

          //便于链式调用
          return this;
        
        },


        
 1 remove : function(){
 2           if(list){
 3             jQuery.each(arguments,function(_,arg){
 4               var index;
 5 
 6               while((index = jQuery.inArray(arg,list,index)) > -1){
 7                  list.splice(index,1);  //用splice()方法删除指定的回调函数
 8                  if(firing){
 9                  /*
10                    firing这个变量只有在调用fire() 这个函数是才是true,,,,,,一结束for循环就为false
11                    //标记正在触发回调
12                    firing = true;
13 
14                    for(;list&& firingIndex < firingLength;firingIndex++){
15                      if(list[firingIndex].apply(data[0],data[1]) === false && options.stopOnFalse){
16                         ...................
17                      }
18                    }
19                    firing = false;
20                    firing     只要回调一结束,,,,,,firing就为false;
21                    
22                  */
23 
24   
25                     /*
26                       如果firing为true时
27                   要让函数正确执行,,,,,,就要动态改变firingLength  和 firingIndex 这两个变量
28                     */
29                     if(index <= firingLength){
30                       firingLength--; 
31                     }
32                     if(index <= firingIndex){
33                       firingIndex--;
34                     }
35                     
36                     
37                  }
38               }
39               
40               
41             })
42           }
43         },

          //has() 这个方法比较简单......

         //has ----> 判断list是否存在指定回调(fn)

        has:function(fn){

          return fn ? jQuery.inArray(fn,list) > -1 : !!(list && list.length);

        },

 

        //empty() ----顾名思义 就是从列表中删除所有回调函数

        empty : function(){

          list = [];

          firingLength =0;

          return this;

        },

 

        //禁用回调列表中的回调

        disable :function(){

          list = stack = memory = undefined;

          return this;

        },

 

        //判断列表是否被禁用

        disabled : function(){  

          return !list;

        },

 

        //锁定列表

        lock : function(){

          stack = undefined;

          if(!memory){

            self.disable();

          }

          return this;

        },

 

        //判断列表是否被锁定

        locked : function(){

          return !stack;

        },

        

        //从下文可以得到,,,,,其实调用fire()  ------ > 实际就是调用的是fireWith() ,,,,,

       

        fireWith : function(context,args){

          if( list && (!fired || stack ) ){

            args = args || [];

 

            args = [ context,args.slice ? args.slice() : args ]

 

            if(firing){

              stack.push( args )

            }else{

              fire(args)

            }

            

 

          }

          return this;

        }

 

 

 

 

        这里我会讲解一下args.slice ? args.slice() : args

        args.slice 的意思就是判断一下args有没有slice这个方法

        有就放回slice()

        无则返回args

        我们要明白一点就是arguments 不是一个真正的数组,,,,,它是一个类数组,,,,所以没有slice()

 

技术分享

 

        上文,,,我已经说过了firing在什么时候为true了

        当正在回调时...firing就是true........所以firing 为true ,,那我们就把参数推入堆栈中,,,等当前回调结束再调用

        stack.push(args);

        如果不是true.....即可直接调用  fire(args)

        

 

        //调用回调函数

        fire : function(){

          self.fireWith(this,arguments);

          return this;

        },

 

        fired: function(){

          return !!fired; //判断回调列表是否执行过

        }

        


      };
    
    return self;
       

  
}


 

 

 

 

 

 

 

 

 

 

 

 

 

本人才疏学浅,,这里是本人看jQuery源码的一些感悟而已,,,,,如有不对的地方,,,欢迎大家留言指出,,共同进步 或者 发邮件至2240970215@qq.com

 

 

 

PS: 路过留点痕。。

如果您看完本篇文章感觉不错,请点击一下右下角的  [推荐] 来支持一下博主,谢谢!原创文章,转载请注明出处!!!

by http://www.cnblogs.com/haoxuebujuan/p/6023546.html

 

jQuery源码解读第5章---对Callbacks的解读