好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

jQuery Callbacks

jQuery Callbacks

jQuery.Callbacks是jQuery的多用途核心组件,专职负责回调函数列队管理,其在jQuery的$.ajax() 和 $.Deferred()提供了一些基础功能。

其主要提供了易于管理的批量回调函数处理的功能。

说到批处理,在Javascript库中屡见不鲜:

Baidu七巧板有 baidu.each() Prototype有 Enumerable#each jQuery有 .each()

等等…………

为了理解Callbacks函数的实现,我们先来了解下jQuery.each()。

each()

我们可以在jQuery的源文件core.js找到其完整实现:

 /*  ********************
 *    obj: 队列列表
 *    callback: 回调函数
 *    args: 回调函数的参数
   */ 
 jQuery. each =  function  ( obj, callback, args ) {
      var   value,
        i  = 0 ,
        length  =  obj.length,
        isArray  = isArraylike( obj );     //  obj是否是类Array对象 

     //  如果有参数 
     if   ( args ) {
          //  如果obj是类Array对象 
         if   ( isArray ) {
              for  ( ; i < length; i++  ) {
                value  =  callback.apply( obj[ i ], args );
                  if  ( value ===  false   ) {
                      break  ;
                }
            }
          //  否则 
        }  else   {
              for  ( i  in   obj ) {
                value  =  callback.apply( obj[ i ], args );
                  if  ( value ===  false   ) {
                      break  ;
                }
            }
        }
      //  如果没有参数,则是一个更为常用的each函数 
    }  else   {
          if   ( isArray ) {
              for  ( ; i < length; i++  ) {
                value  =  callback.call( obj[ i ], i, obj[ i ] );
                  if  ( value ===  false   ) {
                      break  ;
                }
            }
        }   else   {
              for  ( i  in   obj ) {
                value  =  callback.call( obj[ i ], i, obj[ i ] );
                  if  ( value ===  false   ) {
                      break  ;
                }
            }
        }
    }

      return   obj;
} 

借助这个函数我们jQuery实现了其他each函数。如:

$( "li" ).each( function  ( index ) {
  console.log( index  + ": ""  + $(this).text() );
}); 

这里的each实际上只是一个没有args回调函数参数的jQuery.each。

简易Callbacks

实际上对简单的回调函数进行变形,我们也能弄成类似回调函数队列的效果:

 function   dosomething(__callbacks){
      //  do something...... 
     for ( var  i = __callbacks.length; i-- ;){
        __callbacks[i]();
    }
} 

但jQuery.CallBacks可以为我们提供更丰富的功能,和更方便的管理方法。

jQuery.Callbacks(flags)

flags:一个用空格标记分隔的标志可选列表,用来改变回调列表中的行为

once: 确保这个回调列表只执行一次(像一个递延 Deferred). memory: 保持以前的值和将添加到这个列表的后面的最新的值立即执行调用任何回调 (像一个递延 Deferred). unique: 确保一次只能添加一个回调(所以有没有在列表中的重复). stopOnFalse: 当一个回调返回false 时中断调用 callbacks.add(callbacks)

回调列表中添加一个回调或回调的集合。

callbacks:一个函数,或者一个函数数组用来添加到回调列表。

callbacks.remove(callbacks)

删除回调或回调回调列表的集合。

callbacks: 一个函数或函数数组,是从回调列表中删除。  

callbacks.fire(arguments)

调用所有回调函数,并将arguments传给他们。

arguments: 这个参数或参数列表传回给回调列表。

callbacks.disable()

禁用回调列表中的回调。

例子:

 function   fn1( value ){
    console.log( value );
      return   false  ;
}

  function   fn2( value ){
    fn1( "fn2 says:" +  value);
      return   false  ;
}
    
  var  callbacks = $.Callbacks( "unique memory"  );
callbacks.add( fn1 );
callbacks.fire(  "foo"  );
callbacks.add( fn1 );   //  重复添加 
 callbacks.add( fn2 );
callbacks.fire(  "bar"  );
callbacks.add( fn2 );
callbacks.fire(  "baz"  );
callbacks.remove( fn2 );
callbacks.fire(  "foobar"  );

  /*  
output:
foo
fn2 says:foo
bar
fn2 says:bar
baz
fn2 says:baz
foobar
  */ 

optionsCache

我们看到上面的例子中,flags参数是以字符串形式,每个参数以空格间隔,如:

$.Callbacks( "unique memory"  ); 

大家会如何将字符串转成参数呢?

在这里Callbacks会通过正则表达式将字符串转数组,然后再组装成参数对象,如上面的例子,则最后参数对象是:

 {
    unique:   true  ,
    memory:   true  
} 

再将这个对象缓存到optionsCache,以便下次使用。

 //  参数对象缓存 
 var  optionsCache =  {};

  //  将字符串表达转成对象表达,并存在缓存中 
 function   createOptions( options ) {
      var  object = optionsCache[ options ] =  {};
    jQuery.each( options.match( core_rnotwhite )  || [],  function  ( _, flag ) {
        object[ flag ]  =  true  ;
    });
      return   object;
}

jQuery.Callbacks  =  function  ( options ) {
    
      //  通过字符串在optionsCache寻找有没有相应缓存,如果没有则创建一个,有则引用 
     //  如果是对象则通过jQuery.extend深复制后赋给options。 
    options =  typeof  options === "string" ? 
        ( optionsCache[ options ]  ||  createOptions( options ) ) :
        jQuery.extend( {}, options );

      //  ……………… 
     
} 

其他地方的实现比较像一个单一事件的自定义事件处理对象,通过add添加事件处理函数,用remove删除事件处理函数,用fire触发事件。

有兴趣请参照下面的完整备注。 

完整备注

jQuery.Callbacks =  function  ( options ) {

      //  通过字符串在optionsCache寻找有没有相应缓存,如果没有则创建一个,有则引用 
     //  如果是对象则通过jQuery.extend深复制后赋给options。 
    options =  typeof  options === "string" ? 
        ( optionsCache[ options ]  ||  createOptions( options ) ) :
        jQuery.extend( {}, options );

      var   //   最后一次触发回调时传的参数 
         memory,
          //   列表中的函数是否已经回调至少一次 
         fired,
          //   列表中的函数是否正在回调中 
         firing,
          //   回调的起点 
         firingStart,
          //   回调时的循环结尾 
         firingLength,
          //   当前正在回调的函数索引 
         firingIndex,
          //   回调函数列表 
        list =  [],
          //   可重复的回调函数堆栈,用于控制触发回调时的参数列表 
        stack = !options.once &&  [],
          //   触发回调函数列表 
        fire =  function  ( data ) {
              //  如果参数memory为true,则记录data 
            memory = options.memory &&  data;
              //  标记触发回调 
            fired =  true  ;
            firingIndex  = firingStart || 0 ;
            firingStart  = 0 ;
            firingLength  =  list.length;
              //  标记正在触发回调 
            firing =  true  ;
              for  ( ; list && firingIndex < firingLength; firingIndex++  ) {
                  if  ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) ===  false  &&  options.stopOnFalse ) {
                    memory  =  false ;  //   阻止未来可能由于add所产生的回调 
                     break ;     //  由于参数stopOnFalse为true,所以当有回调函数返回值为false时退出循环 
                 }
            }
              //  标记回调结束 
            firing =  false  ;
              //  如果列表存在 
             if   ( list ) {
                  //  如果堆栈存在 
                 if   ( stack ) {
                      //  如果堆栈不为空 
                     if   ( stack.length ) {
                          //  从堆栈头部取出,递归fire。 
                         fire( stack.shift() );
                    }
                  //  否则,如果有记忆 
                }  else   if   ( memory ) {
                      //  列表清空 
                    list =  [];
                  //  再否则阻止回调列表中的回调 
                }  else   {
                    self.disable();
                }
            }
        },
          //   暴露在外的Callbacks对象 
        self =  {
              //   回调列表中添加一个回调或回调的集合。 
            add:  function  () {
                  if   ( list ) {
                      //   首先我们存储当前列表长度 
                     var  start =  list.length;
                    (  function   add( args ) {
                          //  前面我们看到的jQuery.each,对args传进来的列表的每一个对象执行操作 
                        jQuery.each( args,  function  ( _, arg ) {
                              //  得到arg的类型 
                             var  type =  jQuery.type( arg );
                              //  如果是函数 
                             if  ( type === "function"  ) {
                                  //  确保是否可以重复 
                                 if  ( !options.unique || ! self.has( arg ) ) {
                                    list.push( arg );
                                }
                              //  如果是类数组或对象 
                            }  else   if  ( arg && arg.length && type !== "string"  ) {
                                  //  递归 
                                 add( arg );
                            }
                        });
                    })( arguments );
                      //   如果正在回调就将回调时的循环结尾变成现有长度 
                     if   ( firing ) {
                        firingLength  =  list.length;
                      //   如果有memory,我们立刻调用。 
                    }  else   if   ( memory ) {
                        firingStart  =  start;
                        fire( memory );
                    }
                }
                  return   this  ;
            },
              //   从列表删除回调函数 
            remove:  function  () {
                  if   ( list ) {
                      //  继续用jQuery.each,对arguments中的所有参数处理 
                    jQuery.each( arguments,  function  ( _, arg ) {
                          var   index;
                          //  找到arg在列表中的位置 
                         while ( ( index = jQuery.inArray( arg, list, index ) ) > -1  ) {
                              //  根据得到的位置删除列表中的回调函数 
                            list.splice( index, 1  );
                              //  如果正在回调过程中,则调整循环的索引和长度 
                             if   ( firing ) {
                                  if  ( index <=  firingLength ) {
                                    firingLength -- ;
                                }
                                  if  ( index <=  firingIndex ) {
                                    firingIndex -- ;
                                }
                            }
                        }
                    });
                }
                  return   this  ;
            },
              //   回调函数是否在列表中 
            has:  function  ( fn ) {
                  return  jQuery.inArray( fn, list ) > -1 ;
            },
              //   从列表中删除所有回调函数 
            empty:  function  () {
                list  =  [];
                  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;
            },
              //   以给定的上下文和参数调用所有回调函数 
            fireWith:  function  ( context, args ) {
                args  = args ||  [];
                args  = [ context, args.slice ?  args.slice() : args ];
                  if  ( list && ( !fired ||  stack ) ) {
                      //  如果正在回调 
                     if   ( firing ) {
                          //  将参数推入堆栈,等待当前回调结束再调用 
                         stack.push( args );
                      //  否则直接调用 
                    }  else   {
                        fire( args );
                    }
                }
                  return   this  ;
            },
              //   以给定的参数调用所有回调函数 
            fire:  function  () {
                self.fireWith(   this  , arguments );
                  return   this  ;
            },
              //   回调函数列表是否至少被调用一次 
            fired:  function  () {
                  return  !! fired;
            }
        };

      return   self;
}; 

 

 

分类:  Javascript

标签:  Javascript ,  jQuery

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于jQuery Callbacks的详细内容...

  阅读:59次