jQuery源码分析之Callbacks方法

  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 };
原文地址:https://www.cnblogs.com/bender/p/3361142.html