1 (function( jQuery ) { 2 3 // String to Object flags format cache 4 var flagsCache = {}; 5 6 // Convert String-formatted flags into Object-formatted ones and store in cache 7 // 将字符串形式的flags转换成对象形式,并且存到cache中,例: "once memory" => {"once":true,"memory":true} 8 function createFlags( flags ) { 9 var object = flagsCache[ flags ] = {}, 10 i, length; 11 flags = flags.split( /s+/ ); 12 for ( i = 0, length = flags.length; i < length; i++ ) { 13 object[ flags[i] ] = true; 14 } 15 return object; 16 } 17 18 /* 19 * 用以下参数创建一个回调函数列表: 20 * 21 * flags: 可选的以空格分隔的flags,会影响回调函数列表的行为 22 * 23 * 24 * 默认情况下,回调函数列表的行为如同一个事件回调函数列表一样,并且可以触发多次。 25 * 26 * 可选的 flags: 27 * 28 * once: 会确保回调列表只被触发一次(像Deferred一样), 这个不会阻止memory模式,也就是说 "once memory"的参数下 虽然只能fire一次,但是fire后再add 还是有效果的 29 * 30 * memory: 会记录上一次fire的上下文(一般是$.callbacks自身)和参数,并且会立即以最近一次fire记录下的上下文和参数执行新添加进来回调函数 (像Deferred一样) 31 * 具体看 (1) 88行 memory = !flags.memory || [ context, args ]; 保存memory模式下的 函数上下文和参数 32 * (2) 127-130行 self.add添加回调函数时候的处理 33 * 34 * unique: 会确同一个回调函数只在列表中出现一次 35 * 36 * stopOnFalse: 其中一个回调函数返回false即停止调用其余函数 37 * 38 */ 39 jQuery.Callbacks = function( flags ) { 40 41 // Convert flags from String-formatted to Object-formatted 42 // (we check in cache first) 43 flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; 44 45 var // 实际存放回调函数的容器 46 list = [], 47 48 // 用以存放可重复fire(即非once模式),重复fire的回调函数 例: 在一个回调函数体里边也调用了fire的情景 49 // 另外这个stack 还用以标示整个回调列表是否处于锁定状态(仅仅判可不可触发fire, 还是可以add, remove的) 205行 + 192-202行 50 stack = [], 51 52 // 上次fire的上下文和参数, 53 // 可能的值: (1) [context,args] 54 // (2) true , 非记忆执行上下文和参数 或者 stopOnFalse模式下有返回为false的回调函数 55 memory, 56 // 标识当前是否处于fire状态 57 firing, 58 // fire第一个执行的回调函数在list中的索引位置 (内部fireWith使用和add导致list个数变化时修正执行中的索引位置 128行) 59 firingStart, 60 // 循环的结尾位置 (如果在firing状态下self.add添加新函数和self.remove移除函数时候会调整 123行 146行 ) 61 firingLength, 62 // 当前触发的函数索引 ,需要的时候会调整 63 firingIndex, 64 //工具方法: 添加一个或多个(args为数组时)函数到list 65 add = function( args ) { 66 var i, 67 length, 68 elem, 69 type, 70 actual; 71 for ( i = 0, length = args.length; i < length; i++ ) { 72 elem = args[ i ]; 73 type = jQuery.type( elem ); 74 if ( type === "array" ) { 75 // 递归检查 76 add( elem ); 77 } else if ( type === "function" ) { 78 // 非unique模式且新回调函数不存在 79 if ( !flags.unique || !self.has( elem ) ) { 80 list.push( elem ); 81 } 82 } 83 } 84 }, 85 //工具方法: 触发回调函数 86 fire = function( context, args ) { 87 args = args || []; 88 memory = !flags.memory || [ context, args ]; 89 firing = true; 90 firingIndex = firingStart || 0; 91 firingStart = 0; 92 firingLength = list.length; 93 for ( ; list && firingIndex < firingLength; firingIndex++ ) { 94 if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { 95 memory = true; // 标记为终止 注意看106行, 销毁$.callbacks 96 break; 97 } 98 } 99 firing = false; // 标记执行结束 100 if ( list ) { 101 if ( !flags.once ) { // fire完后检查是否有回调函数内部重复fire保留下来的执行上下文和参数 102 if ( stack && stack.length ) { 103 memory = stack.shift(); 104 self.fireWith( memory[ 0 ], memory[ 1 ] ); 105 } 106 } else if ( memory === true ) { // stopOnFalse 107 self.disable(); 108 } else { 109 list = []; 110 } 111 } 112 }, 113 // 实际的回调函数对象 114 self = { 115 //实例方法: 添加一个或多个(args为数组时)函数到list 116 add: function() { 117 if ( list ) { 118 var length = list.length; 119 add( arguments ); 120 // Do we need to add the callbacks to the 121 // current firing batch? 122 if ( firing ) { 123 firingLength = list.length; 124 // With memory, if we're not firing then 125 // we should call right away, unless previous 126 // firing was halted (stopOnFalse) 127 } else if ( memory && memory !== true ) { 128 firingStart = length; 129 fire( memory[ 0 ], memory[ 1 ] ); 130 } 131 } 132 return this; 133 }, 134 // 从列表中移除函数 135 remove: function() { 136 if ( list ) { 137 var args = arguments, 138 argIndex = 0, 139 argLength = args.length; 140 for ( ; argIndex < argLength ; argIndex++ ) { 141 for ( var i = 0; i < list.length; i++ ) { 142 if ( args[ argIndex ] === list[ i ] ) { 143 // 在firing时移除函数,需要修正当前索引firingIndex和长度firingLength 144 if ( firing ) { 145 if ( i <= firingLength ) { 146 firingLength--; 147 if ( i <= firingIndex ) { 148 firingIndex--; 149 } 150 } 151 } 152 // Remove the element 153 list.splice( i--, 1 ); 154 // 如果是unique模式(这时不会有重复的函数),移除一次就可以了 155 if ( flags.unique ) { 156 break; 157 } 158 } 159 } 160 } 161 } 162 return this; 163 }, 164 // 判断指定回调函数是否存在 165 has: function( fn ) { 166 if ( list ) { 167 var i = 0, 168 length = list.length; 169 for ( ; i < length; i++ ) { 170 if ( fn === list[ i ] ) { 171 return true; 172 } 173 } 174 } 175 return false; 176 }, 177 // Remove all callbacks from the list 178 empty: function() { 179 list = []; 180 return this; 181 }, 182 // Have the list do nothing anymore 183 disable: function() { 184 list = stack = memory = undefined; 185 return this; 186 }, 187 // Is it disabled? 188 disabled: function() { 189 return !list; 190 }, 191 // Lock the list in its current state 192 lock: function() { 193 stack = undefined; 194 if ( !memory || memory === true ) { 195 self.disable(); 196 } 197 return this; 198 }, 199 // Is it locked? 200 locked: function() { 201 return !stack; 202 }, 203 // Call all callbacks with the given context and arguments 204 fireWith: function( context, args ) { 205 if ( stack ) { // stack=[] 也是true 206 if ( firing ) { 207 if ( !flags.once ) { 208 stack.push( [ context, args ] ); 209 } 210 } else if ( !( flags.once && memory ) ) { 211 fire( context, args ); 212 } 213 } 214 return this; 215 }, 216 // Call all the callbacks with the given arguments 217 fire: function() { 218 self.fireWith( this, arguments ); 219 return this; 220 }, 221 // To know if the callbacks have already been called at least once 222 fired: function() { 223 return !!memory; // 其实这个有问题, 当调用disable() 的时候 memory==undefined 224 } 225 }; 226 227 return self; 228 }; 229 230 })( jQuery );
下面是一些检验这些参数逻辑的代码:
1 /* 2 jquery.Callbacks 3 */ 4 // var testUrl="http://www.runoob.com/try/ajax/demo_test.php"; 5 // var callbacks=new jQuery.Callbacks("memory"); 6 // callbacks.add(function(){alert("first callback")}); 7 // callbacks.add(function(){alert("second callback")}); 8 // callbacks.fire(); 9 // memory 10 // callbacks.add(function(){alert("third callback")}); 11 12 13 var callback2= 14 jQuery.Callbacks("once memory"), // once 表示回调函数列表只会fire一次,但是还会运行memory机制,也不限制回调函数列表里边有多个相同的函数(可以用unique 去重) 15 something = true; 16 function fn1(args) 17 { 18 alert('fn1 args:'+args); 19 console.log(this); 20 if(something){ 21 callback2.fire(" test:第2次触发"); 22 callback2.fire(" test:第3次触发"); //fire内部再触发的话 先放入stack中 23 something=false; 24 } 25 } 26 function fn2(){ 27 alert('fn2'); 28 } 29 callback2.add(fn1); 30 callback2.add(fn2); 31 32 callback2.fire('测试:第1次触发'); 33 callback2.fire('测试:第4次触发'); //once模式 只会fire一次, memory在add的时候促发 34 callback2.add(fn2);