jquery源码分析之Callbacks

      Callbacks是JQ的一个回调对象,可以对回调进行统一的管理。而且还为Deferred延迟对象提供了基础功能。

  一、举例:

 function aa(){
     alert(1);
 }
 function bb(){
     alert(2);
 }
 setTimeout(function(){
          aa();
    },100);
 bb();

以上代码执行顺序是bb(),然后执行aa(),但是如果我们想要aa()先执行,然后在执行bb()呢?当然我们可以把bb(),添加到setTimeout()中,但是,如果有大量的函数的话,这样会使代码变得臃肿,难于维护。
var ck= $.Callbacks();//Callbacks对象
 function aa(){
     alert(1);
 }
 function bb(){
     alert(2);
 }
 ck.add(bb);  //添加进list数组
 setTimeout(function(){
      aa();  
     ck.fire();  //触发
  },100);
这样代码就会先执行aa(),然后执行bb();

二、.源码解析

  1.$.Callbacks()

当执行$.Callbacks()时,我们看源码


jQuery.Callbacks = function( options ) {

	// Convert options from String-formatted to Object-formatted if needed
	// (we check in cache first)
	options = typeof options === "string" ?
		( optionsCache[ options ] || createOptions( options ) ) :
		jQuery.extend( {}, options );

     1.1我们发现Callbacks函数内部可以传参数,options的值有以下几种情况或者组合形式:

(1)once

      fire()只触发一次

 var ck= $.Callbacks("once");
 function aa(){
     alert(1);
 }
 function bb(){
     alert(2);
 }
 ck.add(aa,bb);
 ck.fire();
 ck.fire();
以上只触发了一次fire()事件。


(2)memory

  记忆功能,记住fire(),如果在fire()触发之后,再继续添加回调,那么直接执行函数。

 var ck= $.Callbacks("memory");
 function aa(){
     alert(1);
 }
 function bb(){
     alert(2);
 }
 ck.add(aa);
 ck.fire();
 ck.add(bb);


这里fire事件触发后,先执行了aa(),ck.add(bb)之后,bb()函数立即触发。如果,没有"memory",那么bb()是不会执行的

(3)unique

  每个函数只会添加一次,不会有重复。

 var ck= $.Callbacks("unique");
 function aa(){
     alert(1);
 }
 function bb(){
     alert(2);
 }
 ck.add(aa);
 ck.add(aa);
 ck.fire();

这里只会执行一次aa(),如果没有unique的话,会执行两遍。


(4)stopOnFalse

当添加的函数中返回false时,中止fire事件。

var ck= $.Callbacks("stopOnFalse");
 function aa(){
     alert(1);
 }
 function bb(){
     alert(2);
     return false;
 }
 ck.add(bb);
 ck.add(aa);
 ck.fire();

bb()函数会执行,aa()不会执行。

  1.2 optionsCache[ options ]与createOptions

// Convert String-formatted options into Object-formatted ones and store in cache
function createOptions( options ) {
	var object = optionsCache[ options ] = {};
	jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
		object[ flag ] = true;
	});
	return object;
}
optionsCache是缓存对象。

createOptions函数会把 “memory  once”这样的options入参变成:object.memory=true;object.once=true;这会在源码中用到。

2.  add()

调用Callbacks函数返回:

return self;

我们看源码,发现self是一个对象。

	// Actual Callbacks object
		self = { 
                   add: function() {},
                   remove: function() {},
                    fireWith:function(){},
                        ......

        }
  
          }

  self里面才是我们Callbacks调用的真实对象,我们下面看下self.add()方法:

     add: function() {
		if ( list ) {    //list初始化为[],所以会进入if
					// First, we save the current length
			var start = list.length;
					(function add( args ) {      //这里使用了匿名自执行,并且传入add()方法的参数对象arguments,这里我对于为什么使用匿   名自执行感觉不太理解,感觉完全没有必要,使用了之后,参数的作用域链变长了,也不需要避免向全局作用域写参数???
						jQuery.each( args, function( _, arg ) {//调用each方法,为args里面的每一个参数绑定一个匿名函数,匿名函数里  面的第一个参数 _ 是索引,第二个参数arg是具体的某一项的值或者引用
							var type = jQuery.type( arg );
							if ( type === "function" ) {   //如果传进来的是函数进入if
								if ( !options.unique || !self.has( arg ) ) {  //如果在Callbacks没有传入unique或者列表list  中没有没有arg,进入if
									list.push( arg );    
								}
							} else if ( arg && arg.length && type !== "string" ) {   //适用于ck.add(aa,bb,cc);的情况
								// Inspect recursively
								add( arg );
							}
						});
					})( arguments );  
				if ( firing ) {
				    firingLength = list.length;
				// With memory, if we're not firing then
				// we should call right away
				 } else if ( memory ) {    //如果Callbacks函数中传入了memory,那么后面再add进来的函数直接触发fire事件
 	                           firingStart = start;
                                   fire( memory );
                                      }
			             }
                                return this;
                           },

3.fireWith()和fire()

当执行ck.fire()时,我们先回触发self.fire()事件。

fire: function() {
	self.fireWith( this, arguments );
	return this;
	},

而self.fire()触发的使self.fireWith()事件。

fireWith: function( context, args ) {
	if ( list && ( !fired || stack ) ) {          //stack = !options.once && [] 这个不需要解释了吧!没有once或者没有执行guofire事件就为true
	   args = args || [];
	   args = [ context, args.slice ? args.slice() : args ];   //context为self对象,args.slice数组拷贝
	    if ( firing ) {        //如果这时正在执行fire事件,我们暂时把这些将要触发的函数,存到stack中。
		stack.push( args );
	     } else {             //否则,直接触发函数
		fire( args );
	           }
	     }
		return this;
	},
       

self.fireWith()调用内部的fire函数

           fire = function( data ) {
			memory = options.memory && data;      //有memory其有要执行的函数
			fired = true;         //fired标记置为true
			firingIndex = firingStart || 0;
			firingStart = 0;
			firingLength = list.length;
			firing = true; //正在触发fire事件
			for ( ; list && firingIndex < firingLength; firingIndex++ ) {
				if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {//data[0]就是self,执行函数list[index],如果执行的函数返回了false并且有stopOnFalse,进入if
					memory = false; // To prevent further calls using add  //把memory置为false,并且跳出循环,不再继续触发。
					break;
				}
			}
			firing = false;
			if ( list ) {   
				if ( stack ) {//如果stack中存着函数引用,
					if ( stack.length ) {
						fire( stack.shift() );//触发stack存储的函数
					}
				} else if ( memory ) {如果有memory
					list = [];
				} else {
					self.disable();//没有memory,后续不再触发fire函数
				}
			}
		},







你可能感兴趣的:(回调,Callbacks,jquery源码,匿名自执行)