注:不解的是走到stack.push分支时是在回调函数里调用fire或fireWidth,但这样会导致死循环,这个stack究竟怎么回事?还请明确的同学告知我一下。!
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 // 假设没有配置once,stack为数组 // stack存放反复时fire參数 stack = !options.once && [], // Fire callbacks fire = function( data ) { //memory 为false或fire时传入的參数 memory = options.memory && data; fired = true; //当前触发位置 firingIndex = firingStart || 0; //触发回调函数队列的起始位置 firingStart = 0; //待触发的回调函数队列长度 firingLength = list.length; //正在触发标识设置为true //仅仅有在Callbacks的回调函数里此值才会为true firing = true; for ( ; list && firingIndex < firingLength; firingIndex++ ) { //若配置了stopOnFalse当回调函数队列中函数返回值为false if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { //memory设置为false memory = false; // To prevent further calls using add //不再运行后面的回调函数 break; } } //回调函数运行结束 firing = false; if ( list ) { //未配置once if ( stack ) { //运行在回调函数里调用的fire if ( stack.length ) { fire( stack.shift() ); } //清空回调函数列表,但此callbacks仍可用 } 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 // 将加入回调函数之前将当前callbacks的长度设为运行開始 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 ); } // 假设arg是个类数组,再递归add } 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方法会走此分支 if ( firing ) { //立马反应要触发回调函数长度的改变 firingLength = list.length; // With memory, if we're not firing then // we should call right away } else if ( memory ) { //改变触发的開始位置为未加入前的回调队列长度 firingStart = start; fire( memory ); } } return this; }, // Remove a callback from the list remove: function() { if ( list ) { jQuery.each( arguments, function( _, arg ) { //arg所在list的索引 var index; while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { //删除 list.splice( index, 1 ); // Handle firing indexes //假设在回调函数里调用remove 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 //锁住当前的状态,不会运行在回调函数里调用fire或fireWidth 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 ]; //?事实上这里有一点不解,走到这个分 //是在在回调函数里调通fire或fireWidth,但这样会导致死循环 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; };