【原创】jQuery1.8.2源码解析之jQuery.Callbacks

时间:2022-03-14 16:07:40
  1 // String to Object options format cache
2 // 是对option的一个缓存,避免每次都要createOptions
3 // 每一个option类似这样
4 // {
5 // memory : true
6 // ,once : true
7 // ,...
8 // }
9 var optionsCache = {};
10
11 // Convert String-formatted options into Object-formatted ones and store in cache
12 function createOptions( options ) {
13 var object = optionsCache[ options ] = {};
14 jQuery.each( options.split( core_rspace ), function( _, flag ) {
15 object[ flag ] = true;
16 });
17 return object;
18 }
19
20 /*
21 * Create a callback list using the following parameters:
22 *
23 * options: an optional list of space-separated options that will change how
24 * the callback list behaves or a more traditional option object
25 *
26 * By default a callback list will act like an event callback list and can be
27 * "fired" multiple times.
28 *
29 * Possible options:
30 *
31 * once: will ensure the callback list can only be fired once (like a Deferred)
32 *
33 * memory: will keep track of previous values and will call any callback added
34 * after the list has been fired right away with the latest "memorized"
35 * values (like a Deferred)
36 *
37 * unique: will ensure a callback can only be added once (no duplicate in the list)
38 *
39 * stopOnFalse: interrupt callings when a callback returns false
40 *
41 */
42 jQuery.Callbacks = function( options ) {
43
44 // Convert options from String-formatted to Object-formatted if needed
45 // (we check in cache first)
46 // options也可以是一个对象
47 options = typeof options === "string" ?
48 ( optionsCache[ options ] || createOptions( options ) ) :
49 jQuery.extend( {}, options );
50
51 var // Last fire value (for non-forgettable lists)
52 memory,
53 // Flag to know if list was already fired
54 fired,
55 // Flag to know if list is currently firing
56 firing,
57 // First callback to fire (used internally by add and fireWith)
58 firingStart,
59 // End of the loop when firing
60 firingLength,
61 // Index of currently firing callback (modified by remove if needed)
62 firingIndex,
63 // Actual callback list
64 list = [],
65 // Stack of fire calls for repeatable lists
66 // 只有在没有设置了once时,stack才存在
67 // stack用来存储参数信息(此时函数列表已经处于firing状态,必须将其他地方调用fire时的参数存储,之后再至此执行fire
68 stack = !options.once && [],
69 // Fire callbacks
70 fire = function( data ) {
71 // 如果设置memory,那么每一次fire的数据将会被保存在memory中,作为下次调用add时参数
72 memory = options.memory && data;
73 fired = true;
74 firingIndex = firingStart || 0;
75 // 重置fireStarting为0,因为add操作(memory)可能改变它
76 firingStart = 0;
77 firingLength = list.length;
78 firing = true;
79 for ( ; list && firingIndex < firingLength; firingIndex++ ) {
80 // 如果设置了stopOnFalse参数,那么当函数列表中有某个函数返回false时,停止后面函数的执行,并且取消memory(如果设置了)
81 if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
82 memory = false; // To prevent further calls using add
83 break;
84 }
85 }
86 firing = false;
87 if ( list ) {
88 // 如果stack存在,那么将之前存储的第一个参数取出,继续fire,直到stack为空
89 if ( stack ) {
90 if ( stack.length ) {
91 fire( stack.shift() );
92 }
93 // 如果stack不存在(即设置了once)
94 // 那么如果设置了memory,那么将之前函数列表清空,也就是说memory还在,add操作可以触发函数立即执行
95 } else if ( memory ) {
96 list = [];
97 } else {
98 self.disable();
99 }
100 }
101 },
102 // Actual Callbacks object
103 // 实际返回的Callbacks对象
104 self = {
105 // Add a callback or a collection of callbacks to the list
106 // 添加函数或者函数集(数组或者伪数组)到函数列表中去
107 add: function() {
108 if ( list ) {
109 // First, we save the current length
110 var start = list.length;
111 (function add( args ) {
112 jQuery.each( args, function( _, arg ) {
113 var type = jQuery.type( arg );
114 // 如果arg是函数
115 // 如果设置unique,则在list中查找是否函数已存在,若存在忽略掉,否则push进list
116 // 如果没有设置unique,则直接push进list
117 if ( type === "function" && ( !options.unique || !self.has( arg ) ) ) {
118 list.push( arg );
119
120 // 如果arg是数组或者伪数组,则递归push进list
121 } else if ( arg && arg.length && type !== "string" ) {
122 // Inspect recursively
123 // 递归(成员为函数集)
124 add( arg );
125 }
126 });
127 })( arguments );
128 // Do we need to add the callbacks to the
129 // current firing batch?
130 // 在添加函数(集)时
131
132 // 如果函数列表正在依次执行回调函数(即firing状态),在某一个callback中执行add(fn)操作
133 // 那么立即修改fireLength以便fire时函数列表能够执行到刚添加的函数(集)
134 if ( firing ) {
135 firingLength = list.length;
136 // With memory, if we're not firing then
137 // we should call right away
138 // 如果不是firing状态,并且设置了memory(肯定是在fired状态时才会执行这一步,因为memory是在fire一次后才会被负值)
139 // 此时memory已经是上次fire是传递的参数,那么将会直接执行刚添加的函数集,而无需fire
140 } else if ( memory ) {
141 firingStart = start;
142 fire( memory );
143 }
144 }
145 return this;
146 },
147 // Remove a callback from the list
148 // 从函数列表中删除函数(集)
149 remove: function() {
150 if ( list ) {
151 jQuery.each( arguments, function( _, arg ) {
152 var index;
153 // while循环的意义在于借助于强大的jQuery.inArray删除函数列表中相同的函数引用(没有设置unique的情况)
154 // jQuery.inArray将每次返回查找到的元素的index作为自己的第三个参数继续进行查找,直到函数列表的尽头
155 // splice删除数组元素,修改数组的结构
156 while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
157 list.splice( index, 1 );
158 // Handle firing indexes
159 // 在函数列表处于firing状态时,最主要的就是维护firingLength和firgingIndex这两个值
160 // 保证fire时函数列表中的函数能够被正确执行(fire中的for循环需要这两个值)
161 if ( firing ) {
162 if ( index <= firingLength ) {
163 firingLength--;
164 }
165 if ( index <= firingIndex ) {
166 firingIndex--;
167 }
168 }
169 }
170 });
171 }
172 return this;
173 },
174 // Control if a given callback is in the list
175 // 判断函数列表只能够是否包含给定的fn
176 has: function( fn ) {
177 return jQuery.inArray( fn, list ) > -1;
178 },
179 // Remove all callbacks from the list
180 // 清空函数列表
181 empty: function() {
182 list = [];
183 return this;
184 },
185 // Have the list do nothing anymore
186 // 使函数列表作废(不能再做任何事情)
187 disable: function() {
188 list = stack = memory = undefined;
189 return this;
190 },
191 // Is it disabled?
192 // 判断是否函数列表是否disabled
193 disabled: function() {
194 return !list;
195 },
196 // Lock the list in its current state
197 lock: function() {
198 stack = undefined;
199 if ( !memory ) {
200 self.disable();
201 }
202 return this;
203 },
204 // Is it locked?
205 locked: function() {
206 return !stack;
207 },
208 // Call all callbacks with the given context and arguments
209 // 和fire相似,不相同的是fireWith可以给定上下文参数
210 // fire中就是调用fireWith中的context就是this(函数列表对象self)
211 fireWith: function( context, args ) {
212 args = args || [];
213 args = [ context, args.slice ? args.slice() : args ];
214 // 首先至少fire一次
215 // 如果执行过一次了(fired),那么若stack存在(即没有设置once),将上下文环境和参数存储,否则什么都不做
216 if ( list && ( !fired || stack ) ) {
217 if ( firing ) {
218 stack.push( args );
219 } else {
220 fire( args );
221 }
222 }
223 return this;
224 },
225 // Call all the callbacks with the given arguments
226 // 依次执行函数列表中的函数
227 fire: function() {
228 self.fireWith( this, arguments );
229 return this;
230 },
231 // To know if the callbacks have already been called at least once
232 // 判断是否函数列表fire过(哪怕只有一次)
233 fired: function() {
234 return !!fired;
235 }
236 };
237
238 return self;
239 };