jQuery.Callbacks之源码解读

  在上一篇jQuery.Callbacks之demo主要说了Callbacks对象初始化常见的选项,这一篇主要分析下Callbacks对象的源代码,对给出两个较为繁琐的demo

 

  1 // String to Object options format cache

  2 var optionsCache = {};

  3 

  4 // Convert String-formatted options into Object-formatted ones and store in cache

  5 /*

  6 这个函数主要将传入的options字符串封装成对象

  7 比如将传入的'once memory'封装成

  8 optionsCache['once memory'] = {

  9     once : true,

 10     memory : true

 11 }

 12 这样方便下次同样的options复用和判断

 13 */

 14 function createOptions( options ) {

 15     var object = optionsCache[ options ] = {};

 16     jQuery.each( options.split( core_rspace ), function( _, flag ) {

 17         object[ flag ] = true;

 18     });

 19     return object;

 20 }

 21 

 22 /*

 23  * Create a callback list using the following parameters:

 24  *

 25  *    options: an optional list of space-separated options that will change how

 26  *            the callback list behaves or a more traditional option object

 27  *

 28  * By default a callback list will act like an event callback list and can be

 29  * "fired" multiple times.

 30  *

 31  * Possible options:

 32  *

 33  *    once:            will ensure the callback list can only be fired once (like a Deferred)

 34  *

 35  *    memory:            will keep track of previous values and will call any callback added

 36  *                    after the list has been fired right away with the latest "memorized"

 37  *                    values (like a Deferred)

 38  *

 39  *    unique:            will ensure a callback can only be added once (no duplicate in the list)

 40  *

 41  *    stopOnFalse:    interrupt callings when a callback returns false

 42  *

 43  */

 44 jQuery.Callbacks = function( options ) {

 45 

 46     // Convert options from String-formatted to Object-formatted if needed

 47     // (we check in cache first)

 48     options = typeof options === "string" ?

 49         ( optionsCache[ options ] || createOptions( options ) ) :

 50         jQuery.extend( {}, options );

 51 

 52     var // Last fire value (for non-forgettable lists)

 53         //大多数情况下这个变量是包含两个元素的数组,[0]表示上次调用的对象,[1]表示上次调用的参数

 54         memory,

 55         // Flag to know if list was already fired

 56         //标识是否执行过回调函数,主要用来实现once

 57         fired,

 58         // Flag to know if list is currently firing

 59         //当前是否在firing,可以参考多线编程中锁的概念,主要用在调用回调函数时,对callbacks对象进行add、remove或者fire,后面会有两个单独的例子说明这种情况

 60         firing,

 61         // First callback to fire (used internally by add and fireWith)

 62         firingStart,

 63         // End of the loop when firing

 64         firingLength,

 65         // Index of currently firing callback (modified by remove if needed)

 66         firingIndex,

 67         // Actual callback list

 68         //所有的回调会被push到这个数组

 69         list = [],

 70         // Stack of fire calls for repeatable lists

 71         //结合firing使用,如果有once选项没什么作用,否则当firing为true时将add或者fire的操作临时存入这个变量,以便于循环完list时继续处理这个变量里面的函数队列

 72         stack = !options.once && [],

 73         // Fire callbacks

 74         fire = function( data ) {

 75             //如果设置memory为true,则将本次的参数data缓存到memory中,用于下次调用

 76             memory = options.memory && data;

 77             fired = true;

 78             //如果options.memory为true,firingStart为上一次Callbacks.add后回调列表的length值

 79             firingIndex = firingStart || 0;

 80             firingStart = 0;

 81             firingLength = list.length;

 82             firing = true;

 83             for ( ; list && firingIndex < firingLength; firingIndex++ ) {

 84                 //如果stopOnFalse为true且本次执行的回调函数返回值为false,则终止回调函数队列的执行

 85                 if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {

 86                     //设置memory为false,防止调用add时会被fire(这个分支是在stopOnFalse memory时被触发)

 87                     memory = false; // To prevent further calls using add

 88                     break;

 89                 }

 90             }

 91             firing = false;

 92             if ( list ) {

 93                 //options.once为false(stack的作用见上)

 94                 if ( stack ) {

 95                     //存在递归的可能,所以不用使用while

 96                     if ( stack.length ) {

 97                         fire( stack.shift() );

 98                     }

 99                 //memory = true, memory = true的情况

100                 } else if ( memory ) {

101                     list = [];

102                 } else {

103                     //once = true, memory = false的情况

104                     self.disable();

105                 }

106             }

107         },

108         // Actual Callbacks object

109         self = {

110             // Add a callback or a collection of callbacks to the list

111             add: function() {

112                 if ( list ) {

113                     // First, we save the current length

114                     var start = list.length;

115                     (function add( args ) {

116                         jQuery.each( args, function( _, arg ) {

117                             var type = jQuery.type( arg );

118                             if ( type === "function" ) {

119                                 //实现unique(回调不唯一 或 唯一且不存在,则push)

120                                 if ( !options.unique || !self.has( arg ) ) {

121                                     list.push( arg );

122                                 }

123                             //如果arg是数组,递归添加回调

124                             } else if ( arg && arg.length && type !== "string" ) {

125                                 // Inspect recursively

126                                 add( arg );

127                             }

128                         });

129                     })( arguments );

130                     // Do we need to add the callbacks to the

131                     // current firing batch?

132                     if ( firing ) {

133                         firingLength = list.length;

134                     // With memory, if we're not firing then

135                     // we should call right away

136                     //如果memory不是false,则直接每次add的时候都自动fire

137                     } else if ( memory ) {

138                         firingStart = start;

139                         fire( memory );

140                     }

141                 }

142                 return this;

143             },

144             // Remove a callback from the list

145             remove: function() {

146                 if ( list ) {

147                     jQuery.each( arguments, function( _, arg ) {

148                         var index;

149                         while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {

150                             list.splice( index, 1 );

151                             // Handle firing indexes

152                             //如果在执行Callbacks.remove操作的状态为firing时则更新firingLength和firingIndex的值

153                             if ( firing ) {

154                                 if ( index <= firingLength ) {

155                                     firingLength--;

156                                 }

157                                 //特殊处理,如果移除的回调的索引小于当前正在执行回调的索引,则firingIdex--

158                                 //后面未执行的回调则得以正常执行

159                                 if ( index <= firingIndex ) {

160                                     firingIndex--;

161                                 }

162                             }

163                         }

164                     });

165                 }

166                 return this;

167             },

168             // Control if a given callback is in the list

169             has: function( fn ) {

170                 return jQuery.inArray( fn, list ) > -1;

171             },

172             // Remove all callbacks from the list

173             empty: function() {

174                 list = [];

175                 return this;

176             },

177             // Have the list do nothing anymore

178             disable: function() {

179                 list = stack = memory = undefined;

180                 return this;

181             },

182             // Is it disabled?

183             disabled: function() {

184                 return !list;

185             },

186             // Lock the list in its current state

187             lock: function() {

188                 stack = undefined;

189                 if ( !memory ) {

190                     self.disable();

191                 }

192                 return this;

193             },

194             // Is it locked?

195             locked: function() {

196                 return !stack;

197             },

198             // Call all callbacks with the given context and arguments

199             fireWith: function( context, args ) {

200                 args = args || [];

201                 args = [ context, args.slice ? args.slice() : args ];

202                 if ( list && ( !fired || stack ) ) {

203                     if ( firing ) {

204                         stack.push( args );

205                     } else {

206                         fire( args );

207                     }

208                 }

209                 return this;

210             },

211             // Call all the callbacks with the given arguments

212             fire: function() {

213                 self.fireWith( this, arguments );

214                 return this;

215             },

216             // To know if the callbacks have already been called at least once

217             fired: function() {

218                 return !!fired;

219             }

220         };

221 

222     return self;

223 };

  需要特殊注意的是有一个firing这个变量,下面给出这个变量的应用场景:

  1、在Callbacks.add中firing为true的情况

 1 // 定义三个将要增加到回调列表的回调函数fn1,fn2,fn3

 2 function fn1(val){

 3     console.log( 'fn1 says ' + val );

 4     //此时Callbacks函数内部的firingLength会自动加1,虽然初始化的Callbacks对象有memory选项,

 5     //但add并不会立即执行fn2,而是等执行完add前的函数队列之后再执行fn2

 6     cbs.add(fn2);

 7 }

 8 function fn2(val){

 9     console.log( 'fn2 says ' + val );

10 }

11 function fn3(val){

12     console.log( 'fn3 says ' + val );

13 }

14 

15 // Callbacks传递了memory

16 // 也可以这样使用$.Callbacks({ memory: true });

17 var cbs = $.Callbacks('memory');

18 

19 // 将fn1增加到回调列表中,因为在fn1中有执行了add(fn2)操作,因此回调列表中的回调为fn1,fn2

20 cbs.add(fn1);

21 

22 //fn1 says foo

23 //fn2 says foo

24 cbs.fire('foo');

25 

26 //将之前fire的参数传递给最近增加的回调fn3,并执行fn3

27 //fn3 says foo

28 cbs.add(fn3);

29 

30 //再执行一次fire,注意此时回调列表中的回调依次是fn1,fn2,fn3,fn2

31 //fn1 says bar

32 //fn2 says bar

33 //fn3 says bar

34 //fn2 says bar

35 cbs.fire('bar');

  2、在Callbacks.fireWith中firing为true的情况

function fn1(val){

    console.log( 'fn1 says ' + val );

}

function fn2(val){

    console.log( 'fn2 says ' + val );

    //此时并不会立即触发cbs里面的回调,而是先把[window, ['bar']]放入stack里面

    //等执行完fireWith前的函数队列之后才执行

    cbs.fireWith(window, ['bar']);

    //firingLength会减一,一定要将当前的函数remove掉,否则会导致死循环

    cbs.remove(fn2);

}



var cbs = $.Callbacks();

cbs.add(fn1);

cbs.add(fn2);

//fn1 says bar

//fn2 says bar

//fn1 says bar

cbs.fire('bar');

 

你可能感兴趣的:(callback)