2012年10月22日星期一

【原创】jQuery1.8.2源码解析之jQuery.Callbacks

【原创】jQuery1.8.2源码解析之jQuery.Callbacks

  1 // String to Object options format cache  2 // 是对option的一个缓存,避免每次都要createOptions  3 // 每一个option类似这样  4 // {  5 //     memory : true  6 //     ,once : true  7 //     ,...  8 // }  9 var optionsCache = {}; 10  11 // Convert String-formatted options into Object-formatted ones and store in cache 12 function createOptions( options ) { 13     var object = optionsCache[ options ] = {}; 14     jQuery.each( options.split( core_rspace ), function( _, flag ) { 15         object[ flag ] = true; 16     }); 17     return object; 18 } 19  20 /* 21  * Create a callback list using the following parameters: 22  * 23  *    options: an optional list of space-separated options that will change how 24  *            the callback list behaves or a more traditional option object 25  * 26  * By default a callback list will act like an event callback list and can be 27  * "fired" multiple times. 28  * 29  * Possible options: 30  * 31  *    once:            will ensure the callback list can only be fired once (like a Deferred) 32  * 33  *    memory:            will keep track of previous values and will call any callback added 34  *                    after the list has been fired right away with the latest "memorized" 35  *                    values (like a Deferred) 36  * 37  *    unique:            will ensure a callback can only be added once (no duplicate in the list) 38  * 39  *    stopOnFalse:    interrupt callings when a callback returns false 40  * 41  */ 42 jQuery.Callbacks = function( options ) { 43  44     // Convert options from String-formatted to Object-formatted if needed 45     // (we check in cache first) 46     // options也可以是一个对象 47     options = typeof options === "string" ? 48         ( optionsCache[ options ] || createOptions( options ) ) : 49         jQuery.extend( {}, options ); 50  51     var // Last fire value (for non-forgettable lists) 52         memory, 53         // Flag to know if list was already fired 54         fired, 55         // Flag to know if list is currently firing 56         firing, 57         // First callback to fire (used internally by add and fireWith) 58         firingStart, 59         // End of the loop when firing 60         firingLength, 61         // Index of currently firing callback (modified by remove if needed) 62         firingIndex, 63         // Actual callback list 64         list = [], 65         // Stack of fire calls for repeatable lists 66         // 只有在没有设置了once时,stack才存在 67         // stack用来存储参数信息(此时函数列表已经处于firing状态,必须将其他地方调用fire时的参数存储,之后再至此执行fire 68         stack = !options.once && [], 69         // Fire callbacks 70         fire = function( data ) { 71             // 如果设置memory,那么每一次fire的数据将会被保存在memory中,作为下次调用add时参数 72             memory = options.memory && data; 73             fired = true; 74             firingIndex = firingStart || 0; 75             // 重置fireStarting为0,因为add操作(memory)可能改变它 76             firingStart = 0; 77             firingLength = list.length; 78             firing = true; 79             for ( ; list && firingIndex < firingLength; firingIndex++ ) { 80                 // 如果设置了stopOnFalse参数,那么当函数列表中有某个函数返回false时,停止后面函数的执行,并且取消memory(如果设置了) 81                 if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { 82                     memory = false; // To prevent further calls using add 83                     break; 84                 } 85             } 86             firing = false; 87             if ( list ) { 88                 // 如果stack存在,那么将之前存储的第一个参数取出,继续fire,直到stack为空 89                 if ( stack ) { 90                     if ( stack.length ) { 91                         fire( stack.shift() ); 92                     } 93                 // 如果stack不存在(即设置了once) 94                 // 那么如果设置了memory,那么将之前函数列表清空,也就是说memory还在,add操作可以触发函数立即执行 95                 } else if ( memory ) { 96                     list = []; 97                 } else { 98                     self.disable(); 99                 }100             }101         },102         // Actual Callbacks object103         // 实际返回的Callbacks对象104         self = {105             // Add a callback or a collection of callbacks to the list106             // 添加函数或者函数集(数组或者伪数组)到函数列表中去107             add: function() {108                 if ( list ) {109                     // First, we save the current length110                     var start = list.length;111                     (function add( args ) {112                         jQuery.each( args, function( _, arg ) {113                             var type = jQuery.type( arg );114                             // 如果arg是函数115                             // 如果设置unique,则在list中查找是否函数已存在,若存在忽略掉,否则push进list116                             // 如果没有设置unique,则直接push进list117                             if ( type === "function" && ( !options.unique || !self.has( arg ) ) ) {118                                 list.push( arg );119 120                             // 如果arg是数组或者伪数组,则递归push进list121                             } else if ( arg && arg.length && type !== "string" ) {122                                 // Inspect recursively123                                 // 递归(成员为函数集)124                                 add( arg );125                             }126                         });127                     })( arguments );128                     // Do we need to add the callbacks to the129                     // current firing batch?130                     // 在添加函数(集)时131 132                     // 如果函数列表正在依次执行回调函数(即firing状态),在某一个callback中执行add(fn)操作133                     // 那么立即修改fireLength以便fire时函数列表能够执行到刚添加的函数(集)134                     if ( firing ) {135                         firingLength = list.length;136                     // With memory, if we're not firing then137                     // we should call right away138                     // 如果不是firing状态,并且设置了memory(肯定是在fired状态时才会执行这一步,因为memory是在fire一次后才会被负值)139                     // 此时memory已经是上次fire是传递的参数,那么将会直接执行刚添加的函数集,而无需fire140                     } else if ( memory ) {141                         firingStart = start;142                         fire( memory );143                     }144                 }145                 return this;146             },147             // Remove a callback from the list148             // 从函数列表中删除函数(集)149             remove: function() {150                 if ( list ) {151                     jQuery.each( arguments, function( _, arg ) {152                         var index;153                         // while循环的意义在于借助于强大的jQuery.inArray删除函数列表中相同的函数引用(没有设置unique的情况)154                         // jQuery.inArray将每次返回查找到的元素的index作为自己的第三个参数继续进行查找,直到函数列表的尽头155                         // splice删除数组元素,修改数组的结构156                         while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {157                             list.splice( index, 1 );158                             // Handle firing indexes159                             // 在函数列表处于firing状态时,最主要的就是维护firingLength和firgingIndex这两个值160                             // 保证fire时函数列表中的函数能够被正确执行(fire中的for循环需要这两个值)161                             if ( firing ) {162                                 if ( index <= firingLength ) {163                                     firingLength--;164                                 }165                                 if ( index <= firingIndex ) {166                                     firingIndex--;167                                 }168                             }169                         }170                     });171                 }172                 return this;173             },174             // Control if a given callback is in the list175             // 判断函数列表只能够是否包含给定的fn176             has: function( fn ) {177                 return jQuery.inArray( fn, list ) > -1;178             },179             // Remove all callbacks from the list180             // 清空函数列表181             empty: function() {182                 list = [];183                 return this;184             },185             // Have the list do nothing anymore186             // 使函数列表作废(不能再做任何事情)187             disable: function() {188                 list = stack = memory = undefined;189                 return this;190             },191             // Is it disabled?192             // 判断是否函数列表是否disabled193             disabled: function() {194                 return !list;195             },196             // Lock the list in its current state197             lock: function() {198                 stack = undefined;199                 if ( !memory ) {200                     self.disable();201                 }202                 return this;203             },204             // Is it locked?205             locked: function() {206                 return !stack;207             },208             // Call all callbacks with the given context and arguments209             // 和fire相似,不相同的是fireWith可以给定上下文参数210             // fire中就是调用fireWith中的context就是this(函数列表对象self)211             fireWith: function( context, args ) {212                 args = args || [];213                 args = [ context, args.slice ? args.slice() : args ];214                 // 首先至少fire一次215                 // 如果执行过一次了(fired),那么若stack存在(即没有设置once),将上下文环境和参数存储,否则什么都不做216                 if ( list && ( !fired || stack ) ) {217                     if ( firing ) {218                         stack.push( args );219                     } else {220                         fire( args );221                     }222                 }223                 return this;224             },225             // Call all the callbacks with the given arguments226             // 依次执行函数列表中的函数227             fire: function() {228                 self.fireWith( this, arguments );229                 return this;230             },231             // To know if the callbacks have already been called at least once232             // 判断是否函数列表fire过(哪怕只有一次)233             fired: function() {234                 return !!fired;235             }236         };237 238     return self;239 };

TAG: