jQuery.Callbacks 一個函數管理器

源碼:


var rnotwhite = (/\S+/g); // String to Object options format cache var optionsCache = {}; // Convert String-formatted options into Object-formatted ones and store in cache function createOptions( options ) { var object = optionsCache[ options ] = {}; jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { object[ flag ] = true; }); return object; } // 上面的代碼是用來將String的參數裝換成對象形式 如:'unique memory' 轉成 {unique:true,memory:true} /* * Create a callback list using the following parameters: * * options: an optional list of space-separated options that will change how * the callback list behaves or a more traditional option object * * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible options: * * once: will ensure the callback list can only be fired once (like a Deferred) * 設置memory就是保存上下文使得後續添加進來的函數可以當即執行且用以前保存的上下文和參數 * memory: will keep track of previous values and will call any callback added * after the list has been fired right away with the latest "memorized" * values (like a Deferred) * * unique: will ensure a callback can only be added once (no duplicate in the list) * * stopOnFalse: interrupt callings when a callback returns false * */ //Callback 中這段代碼主要理解各個選項的做用 和 fire add fireWith 這三個函數基本就明白怎麼回事了。 jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? ( optionsCache[ options ] || createOptions( options ) ) : jQuery.extend( {}, options ); var // Last fire value (for non-forgettable lists) memory, // Flag to know if list was already fired fired, // Flag to know if list is currently firing firing, // First callback to fire (used internally by add and fireWith) firingStart, // End of the loop when firing firingLength, // Index of currently firing callback (modified by remove if needed) firingIndex, // Actual callback list list = [], // Stack of fire calls for repeatable lists stack = !options.once && [], // Fire callbacks fire = function( data ) { memory = options.memory && data; fired = true; firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; firing = true; for ( ; list && firingIndex < firingLength; firingIndex++ ) { //這裏的data[0]是指上下文context data[1] 是指參數數組 看下fireWith函數就知道了 //但選項中有stopOnFalse的時候 函數若是返回false就中止執行隊列中後續的回調函數 if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { memory = false; // To prevent further calls using add break; } } //執行完後將firing設置成false表示已經不是正在執行了 firing = false; if ( list ) { //這裏的stack存放的是後續調用fireWith或者fire的上下文和參數 能夠看fireWith中stack //若是正在執行callback隊列中的函數即firing爲true時 這時再調用fireWith或者fire就會放在stack中排隊 //我的認爲這個變量應該取名爲queue好,由於是先進先出的隊列結構 if ( stack ) { if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) { //這裏若是stack爲false的話 那麼說明設置了once, stack = !options.once && [] 這裏 //能夠看出來 這裏就至關於調用了$.Callback('once memory') 執行一次 後續添加的函數還會當即執行。 list = []; } else { self.disable(); } } }, // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { // First, we save the current length var start = list.length; //這裏有一個當即執行函數的遞歸寫法 以前沒有遇到過 此次學習了 (function add( args ) { jQuery.each( args, function( _, arg ) { var type = jQuery.type( arg ); if ( type === "function" ) { //若是選項中設置了unique if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } } else if ( arg && arg.length && type !== "string" ) { // Inspect recursively add( arg ); } }); })( arguments ); // Do we need to add the callbacks to the // current firing batch? //看是回調函數列表是否正在觸發 若是是那麼只須要修改最後的長度就好了 //由於這樣到時會調用到剛添加進去的回調函數 if ( firing ) { firingLength = list.length; // With memory, if we're not firing then // we should call right away //若是list已經執行完fired 爲真 那麼查看選項中memory是否設置 若是設置那麼當即執行 //剛add的回調函數 } else if ( memory ) { //前面的start在這裏起做用了。若是隊列不正在執行的話.即fired = true 時執行剛添加的函數 //memory 做爲上下文 memory保存了前面fire過的上下文 firingStart = start; fire( memory ); } } return this; }, // Remove a callback from the list //隊列中的函數調用主要使用了數組的一個方法splice remove: function() { if ( list ) { jQuery.each( arguments, function( _, arg ) { var index; while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 ); // Handle firing indexes if ( firing ) { if ( index <= firingLength ) { firingLength--; } if ( index <= firingIndex ) { firingIndex--; } } } }); } return this; }, // Check if a given callback is in the list. // If no argument is given, return whether or not list has callbacks attached. has: function( fn ) { return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); }, // Remove all callbacks from the list empty: function() { list = []; firingLength = 0; return this; }, // Have the list do nothing anymore disable: function() { list = stack = memory = undefined; return this; }, // Is it disabled? disabled: function() { return !list; }, // Lock the list in its current state lock: function() { stack = undefined; if ( !memory ) { self.disable(); } return this; }, // Is it locked? locked: function() { return !stack; }, // Call all callbacks with the given context and arguments fireWith: function( context, args ) { if ( list && ( !fired || stack ) ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; if ( firing ) { stack.push( args ); } else { fire( args ); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once fired: function() { return !!fired; } }; return self; };

參考:$.Callbacks 源碼解析javascript

用jQuery.Callbacks 實現pub/sub 模式html

var topics = {};
jQuery.Topic = function( id ) {
  var callbacks, method,
    topic = id && topics[ id ];
  if ( !topic ) {
    callbacks = jQuery.Callbacks();
    topic = {
      publish: callbacks.fire,
      subscribe: callbacks.add,
      unsubscribe: callbacks.remove
    };
    if ( id ) {
      topics[ id ] = topic;
    }
  }
  return topic;
};

// Subscribers
$.Topic( "mailArrived" ).subscribe( fn1 );
$.Topic( "mailArrived" ).subscribe( fn2 );
$.Topic( "mailSent" ).subscribe( fn1 );
// Publisher
$.Topic( "mailArrived" ).publish( "hello world!" );
$.Topic( "mailSent" ).publish( "woo! mail!" );
相關文章
相關標籤/搜索