jQuery.Callbacks方法非常重要,它在jQuery源码中被多次使用,如在ready方法、Deferred对象中。它主要的作用是提供管理和操作回调函数列表方法,增删回调函数,遍历执行回调函数,设置回调函数执行的上下文环境和参数等。它封装内部核心逻辑代码,返回闭包self对象供外部访问/操作回调函数队列list。
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 };