/* * once: 确保回调列表仅只fire一次 * unique: 在执行add操做中,确保回调列表中不存在重复的回调 * stopOnFalse: 当执行回调返回值为false,则终止回调队列的执行 * momery: 记录上一次fire时的参数,并在add中传递给fire和执行fire,执行时firingIndex为上一次fire时的firingLength */
var optionsCache = {}, // Used for splitting on whitespace core_rnotwhite = /\S+/g; // Convert String-formatted options into Object-formatted ones and store in cache function createOptions( options ) { // 多个变量指向同一对象(或数组)引用时,其中一个变量修改了被引用对象的内部结构,其余引用变量也会表现出来 var object = optionsCache[ options ] = {}; jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { object[ flag ] = true; // optionsCache[ options ][ flag ] = true; }); return object; } 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 ] ( optionsCache[ options ] || createOptions( options ) ) : // 说明也能够这样$.Callbacks({once:true, memory:true})使用 jQuery.extend( {}, options ); var // Flag to know if list is currently firing firing, // Last fire value (for non-forgettable lists) memory, // Flag to know if list was already fired fired, // End of the loop when firing firingLength, // Index of currently firing callback (modified by remove if needed) firingIndex, // First callback to fire (used internally by add and fireWith) firingStart, // Actual callback list list = [], // Stack of fire calls for repeatable lists stack = !options.once && [], // Fire callbacks // data为fireWith内部整理的args数组 fire = function( data ) { memory = options.memory && data; fired = true; // 处理在add中,options.memory = true;的状况 firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; firing = true; for ( ; list && firingIndex < firingLength; firingIndex++ ) { // 正在执行的回调返回值为false 且 options.stopOnFalse为true,则终止回调队列的执行 if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { memory = false; // To prevent further calls using add break; } } firing = false; if ( list ) { // 处理正在执行的回调中执行fireWith的操做; if ( stack ) { if ( stack.length ) { fire( stack.shift() ); } } // 上一分支状态为回调执行过,且能够执行屡次 // 此时 options.once = true; 这里将list设置为[],只是确保下次执行fire时,无回调执行 // 可是若是 options.memory = true; 仍然会执行add中的fire操做,由于此时回调列表中已有回调 else if ( 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" ) { // 回调不惟一 或 惟一且不存在,则push 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? // 正在执行的回调执行了add操做,则更新firingLength if ( firing ) { firingLength = list.length; // With memory, if we're not firing then // we should call right away // 若是options.memory为true,则再次执行fire,且参数相同,fire中的firingIndex为此时的firingStart } else if ( memory ) { firingStart = start; fire( memory ); } } return this; }, // Remove a callback from the list 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 // 正在执行的回调执行了remvoe操做,则更新firingLength和firingIndex的值 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 = []; return this; }, // Have the list do nothing anymore // 禁用add,remove,fire主要方法的工做 disable: function() { list = stack = memory = undefined; return this; }, // Is it disabled? disabled: function() { return !list; }, // Lock the list in its current state lock: function() { // 若是回调执行过,则将阻止self.fire操做 // 但若是 options.memory = true,则仍然会执行fire操做 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 ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; // 回调未执行 或 已执行且能够执行屡次 if ( list && ( !fired || stack ) ) { // 正在执行的回调函数执行了fireWith操做( 暗指回调列表已执行过,且能够执行屡次,stack = []; ) // 该函数须要条件执行,或有移除该函数的操做,不然陷入死循环,详见例2 if ( firing ) { stack.push( args ); } // 正在执行的回调函数没有执行fireWith操做 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; };
例1:数组
$(function(){ // 定义三个将要增长到回调列表的回调函数fn1,fn2,fn3 function fn1(arg){ console.log( 'fn1 says:' + arg ); // 在fn1中执行Callbacks.add操做,此时Callbacks函数内部的firingLength将会获得更新 $callbacks.add(fn2); } function fn2(arg){ console.log( 'fn2 says:' + arg ); } function fn3(arg){ console.log( 'fn3 says:' + arg ); } // Callbacks传递了memory // 也能够这样使用$.Callbacks({ memory: true }); var $callbacks = $.Callbacks('memory'); // 将fn1增长到回调列表中,由于在fn1中有执行了add(fn2)操做,所以回调列表中的回调为fn1,fn2 $callbacks.add(fn1); // output: fn1 says:foo // output: fn2 says:foo $callbacks.fire('foo'); // 将以前fire的参数传递给最近增长的回调fn3,并执行fn3 // output: fn3 says:foo $callbacks.add(fn3); // 再执行一次fire,注意此时回调列表中的回调一次是fn1,fn2,fn3,fn2 // output: fn1 says:baz // output: fn2 says:baz // output: fn3 says:baz // output: fn2 says:baz // 若是指望回调列表中只有fn1,fn2,fn3,只需在Callbacks函数中传入unique $callbacks.fire('baz'); });
例2缓存
$(function(){ function fn1(arg){ console.log( 'fn1 says:' + arg ); } function fn2(arg){ console.log( 'fn2 says:' + arg ); $callbacks.fireWith(window, ['yjh']); // 必定要执行这一步,不然将会陷入死循环 $callbacks.remove(fn2); } var $callbacks = $.Callbacks(); $callbacks.add(fn1); // output: fn1 says:foo $callbacks.fire('foo'); $callbacks.add(fn2); // output: fn1 says:baz // output: fn2 says:baz // output: fn1 says:yjh $callbacks.fire('baz'); });
PS:app
此前写过一篇关于jQuery.Callbacks源码分析的随笔,理解不透彻,今天又从新翻阅了一下,记录一下本身的源码阅读,相比以前,感受好多了。ide
阅读前,能够先看API,弄清楚四个参数标志,'once', 'memory', 'unique', 'stopOnFalse', 简单的执行add, fire操做,而后再看源码;函数
阅读顺序:oop
一、先阅读var声明的变量,fire函数的前半部分,self对象中的add, remove函数,有些难以理解暂时往下看;源码分析
二、而后阅读self对象中的fire,fireWith,最后再来阅读fire函数,弄清楚后再看其余self对象中的方法。this