1 // String to Object flags format cache 2 // 我们暂且称flagsCache为行为标识符串信息集合,供jQuery.Callbacks函数使用 3 // flagCache存储信息格式如下: 4 // {'once':{'once':true},'once memory':{'once':true,'memory':true}} 5 // 其中key为行为标识符串,value为行为标识符串所包含的行为标识符的信息对象 6 // 行为标识符串是由空格隔开的多个行为标识符构成的字符串 7 // 行为标识符则是影响队列里的回调函数执行方式的字符串 8 var flagsCache = {}; 9 10 // Convert String-formatted flags into Object-formatted ones and store in cache 11 // 将行为标识符串转换成对象存储到flagsCache中,供jQuery.Callbacks函数使用 12 function createFlags( flags ) { 13 var object = flagsCache[ flags ] = {}, 14 i, length; 15 flags = flags.split( /s+/ ); 16 for ( i = 0, length = flags.length; i < length; i++ ) { 17 object[ flags[i] ] = true; 18 } 19 return object; 20 } 21 22 /* 23 * Create a callback list using the following parameters: 24 *flags:an optional list of space-separated flags that will change how 25 *the callback list behaves 26 * By default a callback list will act like an event callback list and can be 27 * "fired" multiple times. 28 * Possible flags: 29 *once:will ensure the callback list can only be fired once (like a Deferred) 30 * 31 *memory: will keep track of previous values and will call any callback added 32 * after the list has been fired right away with the latest "memorized" 33 * values (like a Deferred) 34 * 35 *unique: will ensure a callback can only be added once (no duplicate in the list) 36 * 37 *stopOnFalse: interrupt callings when a callback returns false 38 */ 39 //以上英文为jQuery自带API说明,现用自己的语言说明一下: 40 //该函数将创建用于管理回调函数列表的对象 41 //参数flags为行为标识字符串,是由空格隔开的多个行为标识符构成的字符串。 42 //默认情况下(即不传flags),则回调函数列表如同事件回调函数列表般执行。 43 //flags可能的值: 44 //(1)once:该行为标识符表示列表中的回调函数仅执行一次 45 //(2)memory:该行为标识符表示会将先前执行回调函数用到的值(context和args)放入栈中缓存 46 //(3)unique:该行为标识符表示确保放入列表中的回调函数唯一性 47 //(4)stopOnFalse:该行为标识符表示当执行列表中的回调函数返回false时将中断后面的回调函数执行 48 jQuery.Callbacks = function( flags ) { 49 // Convert flags from String-formatted to Object-formatted 50 // (we check in cache first) 51 // 获取行为标识符信息对象 52 flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; 53 54 var // Actual callback list 55 // 存放回调函数的列表 56 list = [], 57 // Stack of fire calls for repeatable lists 58 // 存放调用回调函数所需参数(context和args)放入队列中 59 stack = [], 60 // Last fire value (for non-forgettable lists) 61 // 缓存最近一次执行回调函数列表所用到的值(context和args) 62 memory, 63 // Flag to know if list is currently firing 64 // 标记是否回调函数列表正在执行 65 firing, 66 // First callback to fire (used internally by add and fireWith) 67 // 标记回调函数列表执行开始的下标值 68 firingStart, 69 // End of the loop when firing 70 // 标记回调函数列表执行结束的下标值 71 firingLength, 72 // Index of currently firing callback (modified by remove if needed) 73 // 标记回调函数列表正在执行的下标值 74 firingIndex, 75 // Add one or several callbacks to the list 76 // 向回调函数列表添加函数 77 add = function( args ) { 78 var i, 79 length, 80 elem, 81 type, 82 actual; 83 for ( i = 0, length = args.length; i < length; i++ ) { 84 elem = args[ i ]; 85 type = jQuery.type( elem ); 86 if ( type === "array" ) { 87 // Inspect recursively 88 // 若元素是数组,则递归,直到元素类型为方法时才放入列表中 89 add( elem ); 90 } else if ( type === "function" ) { 91 // Add if not in unique mode and callback is not in 92 // 直到elem为方法时, 93 // 判断unique标识符是否传入,若是则需判断elem是否已存在列表中; 94 // 若非唯一,则执行将elem放入列表中。 95 if ( !flags.unique || !self.has( elem ) ) { 96 list.push( elem ); 97 } 98 } 99 } 100 }, 101 // Fire callbacks 102 // 执行回调函数列表 103 // context为上下文环境,必选 104 // args为传入回调函数参数,可选 105 fire = function( context, args ) { 106 args = args || []; 107 //判断memory标识符是否传入, 108 //如是则memory=[context, args], 109 //若非则memory=true 110 memory = !flags.memory || [ context, args ]; 111 //标记列表正在执行 112 firing = true; 113 //标记列表执行正在执行的下标值 114 firingIndex = firingStart || 0; 115 //重置列表执行开始位的下标值 116 firingStart = 0; 117 //标记列表执行长度 118 firingLength = list.length; 119 for ( ; list && firingIndex < firingLength; firingIndex++ ) { 120 //需判断stopOnFalse标识符是否传入, 121 //如是则在执行回调函数返回false时中断列表后面的回调函数执行并标记memory=true 122 if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { 123 memory = true; // Mark as halted 124 break; 125 } 126 } 127 //标记列表执行完毕 128 firing = false; 129 if ( list ) { 130 //判断once行为标识符是否传入 131 if ( !flags.once ) { 132 //未传入once行为标识符则判断队列stack是否存值 133 if ( stack && stack.length ) { 134 //弹出队列的第一个元素作为调用回调函数列表所需参数(context和args)传入 135 memory = stack.shift(); 136 self.fireWith( memory[ 0 ], memory[ 1 ] ); 137 } 138 } else if ( memory === true ) { 139 //传入once行为标识符,并且memory=true。 140 //执行列表的回调函数后,memory=true只有两种情况: 141 //1、memory标识符未传入(即行为标识符串flags包含有memory)。 142 //2、stopOnFalse标识符传入(即行为标识符串(flags)包含有stopOnFalse), 143 //并且执行列表回调函数时返回false。 144 //此时回调函数列表将被冻结使用,即后面对该列表任何操作都将失效。 145 self.disable(); 146 } else { 147 //传入once行为标识符,则置空回调函数列表 148 list = []; 149 } 150 } 151 }, 152 // Actual Callbacks object 153 // self为函数jQuery.Callbacks所要返回的对象,其实它是闭包。 154 // 函数外部是无法访问的函数jQuery.Callbacks内的变量和函数的。 155 // 为了能够提供外部操作回调函数列表,故将self对象返回。 156 // self对象的一些方法返回this的目的是为了能够链式调用,这是一个技巧。 157 self = { 158 // Add a callback or a collection of callbacks to the list 159 // 添加回调函数到列表中 160 add: function() { 161 if ( list ) { 162 var length = list.length; 163 //调用上面定义的add函数 164 add( arguments ); 165 // Do we need to add the callbacks to the 166 // current firing batch? 167 // 判断回调函数列表是否正在执行 168 if ( firing ) { 169 //如是,则需将firingLength重置为添加元素后的列表长度 170 //新增的回调函数能够被执行到,具体原因见上面的函数fire实现 171 firingLength = list.length; 172 } 173 // 如非,则判断memory是否缓存有最近一次执行回调函数列表时所用的值(context和args) 174 else if ( memory && memory !== true ) { 175 // With memory, if we're not firing then 176 // we should call right away, unless previous 177 // firing was halted (stopOnFalse) 178 // 如是,则将回调函数列表执行的开始下标设置为新增的元素下标值, 179 // 并将memory缓存的两个值作为参数传给fire函数调用 180 firingStart = length; 181 fire( memory[ 0 ], memory[ 1 ] ); 182 } 183 } 184 return this; 185 }, 186 // Remove a callback from the list 187 // 移除指定回调函数列表中指定的元素 188 remove: function() { 189 if ( list ) { 190 var args = arguments, 191 argIndex = 0, 192 argLength = args.length; 193 for ( ; argIndex < argLength ; argIndex++ ) { 194 // 删除指定的元素值每次都需遍历一遍回调函数列表list 195 for ( var i = 0; i < list.length; i++ ) { 196 if ( args[ argIndex ] === list[ i ] ) { 197 // Handle firingIndex and firingLength 198 // 若回调函数列表正在执行中 199 // 则需相应的设置firingIndex和firingLength的值 200 if ( firing ) { 201 if ( i <= firingLength ) { 202 firingLength--; 203 if ( i <= firingIndex ) { 204 firingIndex--; 205 } 206 } 207 } 208 // Remove the element 209 // 由于执行splice列表的元素值将减1 210 // 所以当删除操作执行完后索引变量i也需减1 211 list.splice( i--, 1 ); 212 // If we have some unicity property then 213 // we only need to do this once 214 // 判断unique行为标识符是否传入 215 // 如是则无需继续遍历查询需删除的元素是否在列表中 216 // 因为unique行为标识符已确保列表中元素的唯一性 217 // 这是一个技巧 218 if ( flags.unique ) { 219 break; 220 } 221 } 222 } 223 } 224 } 225 return this; 226 }, 227 // Control if a given callback is in the list 228 // 检测是否回调函数队列中是否已包含有指定回调函数 229 has: function( fn ) { 230 if ( list ) { 231 var i = 0, 232 length = list.length; 233 for ( ; i < length; i++ ) { 234 if ( fn === list[ i ] ) { 235 return true; 236 } 237 } 238 } 239 return false; 240 }, 241 // Remove all callbacks from the list 242 // 置空回调函数队列list 243 empty: function() { 244 list = []; 245 return this; 246 }, 247 // Have the list do nothing anymore 248 // 调用次函数后,终结对回调函数列表的任何操作 249 disable: function() { 250 list = stack = memory = undefined; 251 return this; 252 }, 253 // Is it disabled? 254 disabled: function() { 255 return !list; 256 }, 257 // Lock the list in its current state 258 // 锁住回调函数列表的当前调用状态(context和args) 259 lock: function() { 260 stack = undefined; 261 if ( !memory || memory === true ) { 262 self.disable(); 263 } 264 return this; 265 }, 266 // Is it locked? 267 // 判定是否已锁住回调函数列表的当前调用状态(context和args) 268 locked: function() { 269 return !stack; 270 }, 271 // Call all callbacks with the given context and arguments 272 // 使用指定的参数(context和args)调回调函数列表 273 fireWith: function( context, args ) { 274 if ( stack ) { 275 if ( firing ) { 276 //若回调函数列表正在执行 277 if ( !flags.once ) { 278 //若未传入once行为标识符 279 //由于回调函数列表正在执行, 280 //所以回调函数列表不能立即使用传入的参数(context和args)执行, 281 //需要将传入的参数(context和args)存入队列stack中。 282 //当回调函数列表执行完毕后,将会逐一使用队列stack中的值再执行回调函数列表,直到队列stack没有元素为止 283 //具体请参看fire函数 284 stack.push( [ context, args ] ); 285 } 286 } else if ( !( flags.once && memory ) ) { 287 //若回调函数列表已执行完毕,则直接使用传入的参数(context和args)调用回调函数列表 288 fire( context, args ); 289 } 290 } 291 return this; 292 }, 293 // Call all the callbacks with the given arguments 294 // fire方法是fireWith的特殊化,将fire方法的调用对象即self本身作为context传入fireWith方法中 295 fire: function() { 296 self.fireWith( this, arguments ); 297 return this; 298 }, 299 // To know if the callbacks have already been called at least once 300 fired: function() { 301 return !!memory; 302 } 303 }; 304 return self; 305 };