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 ) { // stopOnFalse107 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 the121 // current firing batch?122 if ( firing ) {123 firingLength = list.length;124 // With memory, if we're not firing then125 // we should call right away, unless previous126 // 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和长度firingLength144 if ( firing ) {145 if ( i <= firingLength ) {146 firingLength--;147 if ( i <= firingIndex ) {148 firingIndex--;149 }150 }151 }152 // Remove the element153 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 list178 empty: function() {179 list = [];180 return this;181 },182 // Have the list do nothing anymore183 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 state192 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 arguments204 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 arguments217 fire: function() {218 self.fireWith( this, arguments );219 return this;220 },221 // To know if the callbacks have already been called at least once222 fired: function() {223 return !!memory; // 其实这个有问题, 当调用disable() 的时候 memory==undefined224 }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 // memory10 // 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);