工具方法。对函数的统一管理。html
jquery2.0.3版本$.Callback()部分的源码以下:jquery
// 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( core_rnotwhite ) || [], function( _, flag ) { object[ flag ] = true; }); return object; } /* * 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: 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 * */ 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++ ) { if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { memory = false; // To prevent further calls using add break; } } firing = false; if ( list ) { if ( stack ) { if ( stack.length ) { fire( stack.shift() ); } } 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" ) { 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 } 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 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; };
观察者模式,添加完后统一触发。数组
function aaa(){ alert(1); } function bbb(){ alert(2); } var cb= $.Callbacks(); cb.add(aaa); cb.add(bbb); cb.fire();
要统一的管理aaa和bbb。有时候以下,很难对不一样做用域下的函数进行统一管理。数据结构
function aaa(){ alert(1); } (function(){ function bbb(){ alert(2); } })(); aaa(); bbb();
只能弹出1,由于bbb是局部做用域中的。app
$callback能够作到。以下,只要cb是全局的。ide
var cb= $.Callbacks(); function aaa(){ alert(1); } cb.add(aaa); (function(){ function bbb(){ alert(2); } cb.add(bbb); })(); cb.fire();
对应复杂状况颇有用。统一管理,经过fire统一触发。函数
Callback接收一个参数,能够有4个选项,once,memory,unique,stopOnFalse。工具
self单体有这些方法:add,remove,has,empty,disable,disabled,lock,locked, fireWith,fire,fired。oop
list=[]数组变量,用来收集回调函数。fire的时候对其循环调用。学习
add:push数组
fire:调用fireWith,fireWith容许传参,fire可传可不传。
fireWith:调用私有函数fire,在私有函数fire中for循环list。
remove:splice数组。
4个参数:
举例:
不传参数,fire几回就触发几回。
function aaa() { alert(1); } function bbb() { alert(2); } var cb = $.Callbacks(); cb.add(aaa); cb.add(bbb); cb.fire(); //1 2 cb.fire();//1 2
function aaa() { alert(1); } function bbb() { alert(2); } var cb = $.Callbacks('once'); cb.add(aaa); cb.add(bbb); cb.fire(); //1 2 cb.fire();
不传参数,在fire以后add的回调不能被fire。
//不写参数,只弹出1,2不会弹出 function aaa() { alert(1); } function bbb() { alert(2); } var cb = $.Callbacks(); cb.add(aaa); cb.fire(); //1 cb.add(bbb);
function aaa() { alert(1); } function bbb() { alert(2); } var cb = $.Callbacks('memory'); cb.add(aaa); cb.fire(); //1 2 cb.add(bbb);
//不加参数,add2次aaa,就会触发2次aaa function aaa() { alert(1); } var cb = $.Callbacks(); cb.add(aaa); cb.add(aaa); cb.fire(); //1 1
function aaa() { alert(1); } var cb = $.Callbacks('unique'); cb.add(aaa); cb.add(aaa); cb.fire(); //1 加了unique参数,一样的函数不能屡次add
function aaa() { alert(1); return false; } function bbb() { alert(2); } var cb = $.Callbacks(); cb.add(aaa); cb.add(bbb); cb.fire(); //1 2 不传参,第一个函数返回false时后面的函数也能正常执行
function aaa() { alert(1); return false; } function bbb() { alert(2); } var cb = $.Callbacks('stopOnFalse'); cb.add(aaa); cb.add(bbb); cb.fire(); //1 //传参stopOnFalse,第一个函数返回false时后面的函数再也不执行
function aaa() { alert(1); } function bbb() { alert(2); } //组合使用,只执行一次,而且弹出1 2 var cb = $.Callbacks('once memory'); cb.add(aaa); cb.fire(); //1 cb.add(bbb); cb.fire();
源码中:
传入了 once和memory后,
options={once:true,memory:true} optionCache={ "once memory":{once:true,memory:true} }
参数做为每一个回调函数的实参
function aaa(n) { alert("aaa "+n); } function bbb(n) { alert("bbb "+n); } var cb = $.Callbacks(); cb.add(aaa); cb.add(bbb); //fire传参 cb.fire("hello"); //弹出aaa hello 和bbb hello
Callbacks就是一个工具函数,内部定义了一个self ,add和remove还有has等挂在self上。
$.Callbacks有4个可选的参数,能够组合传入,用空格分隔。好比 $.Callbacks("once memory unique");
这样传入的构造函数字符串其实是一个字符串,源码中作了处理会把这个字符串转成对象。
// 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( core_rnotwhite ) || [], function( _, flag ) { object[ flag ] = true; }); return object; }
在构造函数中传入一个options后,先进行以下处理调用。把一个字符串处理成一个对象。
传入的options="once memory unique"处理后options={once:true,memory:true,unique:true}。
// 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 );
过程以下:options="once memory unique"是string类型,因此先从optionsCache中获取,如今optionsCache为{}因此optionsCache[ options ]是undefined走后面的createOptions( options ) 。create操做中先新建一个以options为键的空对象,再循环给对象中填充。循环操做完
optionCache为
optionCache={ "once memory unique":{once:true,memory:true,unique:true} }
options为
options={once:true,memory:true,unique:true}
主要是把回调函数Push到数组list中。
add: function() { if ( list ) { //list初始化为[],if判断会返回true // First, we save the current length var start = list.length; (function add( args ) { jQuery.each( args, function( _, arg ) { ////处理cb.add(aaa,bbb)这种调用 var type = jQuery.type( arg );//arg就是每个函数 if ( type === "function" ) {//arg是函数就push到list中,此时有个判断有没有unique if ( !options.unique || !self.has( arg ) ) {//有unique走后面,判断list中有没有这个函数,有就不添加了 list.push( arg ); } } else if ( arg && arg.length && type !== "string" ) { //处理cb.add([aaa,bbb])这种调用 // Inspect recursively add( arg );//递归分解,最终仍是push到list } }); })( 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 } 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 );//主要就是splice删除操做 // Handle firing indexes if ( firing ) { if ( index <= firingLength ) { firingLength--; } if ( index <= firingIndex ) { firingIndex--; } } } }); } return this; },
self的fire调用self的fireWith,fireWith把参数传递到fire()函数。
// 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; },
fire()时主要是for循环
// Fire callbacks fire = function( data ) { memory = options.memory && data; fired = true;//fired变为true说明已经调用过一次了, firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; firing = true;//触发进行时 for ( ; list && firingIndex < firingLength; firingIndex++ ) { if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {//每次函数调用同时处理stopOnFalse的状况 memory = false; // To prevent further calls using add //stopOnFalse后有memory也很差使了 break; } } firing = false;//触发结束 if ( list ) { if ( stack ) { if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) { list = []; } else { self.disable(); } } },
好比在 fire 处理队列中,某个函数又在队列中添加了一个回调函数,或者,在队列中又删除了某个回调函数。 fire 处理过程当中,某个函数又调用了 fire 来触发事件呢?
先经过例子来看一下效果
function aaa() { alert(1); cb.fire(); //在这里调用fire()会出现什么问题 死循环 } function bbb() { alert(2); } var cb = $.Callbacks(); cb.add(aaa); cb.add(bbb); cb.fire();
在执行函数的过程当中再次调用fire()的执行顺序是怎样的?
var bBtn=true;//用bBtn避免死循环 function aaa() { alert(1); if(bBtn){ cb.fire();//注意这里fire调用后执行顺序是1 2 1 2,而不是1 1 2 2 bBtn=false; } } function bbb() { alert(2); } var cb = $.Callbacks(); cb.add(aaa); cb.add(bbb); cb.fire();
结论:把函数运行过程当中触发的fire()放到了运行过程的队列当中。
fire 处理过程当中,某个函数又调用了 fire 来触发事件时,jQuery的处理方式以下:
将这个嵌套的事件先保存起来,等到当前的回调序列处理完成以后,再检查被保存的事件,继续完成处理。显然,使用队列是处理这种状况的理想数据结构,若是遇到这种情况,咱们就将事件数据入队,待处理的时候,依次出队数据进行处理。何时须要这种处理呢?显然不是once的状况。在JavaScript中,堆队列也是经过数组来实现的,push用来将数据追加到数组的最后,而shift用来出队,从数据的最前面获取数据。
不过,jQuery没有称之为队列,而是取名stack。
// Stack of fire calls for repeatable lists stack = !options.once && [],
入队
源码中,在fireWith的时候判断for循环有没有执行完
fireWith: function( context, args ) { ...if ( firing ) {//firing在for循环没有走完时一直是true stack.push( args );//因此这句话意思就是函数执行时再去fire()调用就会push到stack数组中 } else { fire( args ); } } return this; },
出队
再去调用fire()的时候
// Fire callbacks fire = function( data ) { memory = options.memory && data; fired = true;//fired变为true说明已经调用过一次了, firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; firing = true;//触发进行时 for ( ; list && firingIndex < firingLength; firingIndex++ ) { if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {//每次函数调用同时处理stopOnFalse的状况 memory = false; // To prevent further calls using add //stopOnFalse后有memory也很差使了 break; } } firing = false;//触发结束 if ( list ) { if ( stack ) { //这就是出如今函数执行过程当中再次fire()的时候,等循环执行完,再去按顺序执行 if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) {//只执行一次的时候,有once,memory就清空list,此时fire()就至关于一个执行一个空数组 list = []; } else { self.disable();//disable阻止后续任何的fire()操做 } } },
针对下面这段源码的一个例子:
once和memory同时存在的时候,fire()无效由于list为[]了,可是add仍然有效。
当有memory的时候,把以前添加的清空;容许添加并再次运行fire后清空;当不存在memory的时候既只有once配置,fire以后既不容许作任何操做了。
else if ( memory ) {//只执行一次的时候,有once,memory就清空list,此时fire()就至关于一个执行一个空数组 list = []; } else { self.disable();//disable阻止后续任何的fire()操做 }
disable阻止后续任何的fire()操做。
function aaa() { alert(1); } function bbb() { alert(2); } //组合使用,只执行一次,而且弹出1 2 3 var cb = $.Callbacks('once memory'); cb.add(aaa); cb.fire(); //1 cb.fire();//此时list为[] cb.add(bbb); cb.fire(); function ccc(){ alert(3); } cb.add(ccc);
has(fn):判断list有没有fn
empty: 清空数组list=[]
disable:所有锁住,禁止了,以下
// Have the list do nothing anymore disable: function() { list = stack = memory = undefined; return this; },
disabled:判断是否是禁止了。return !list;
lock:只是把stack锁住
// Lock the list in its current state lock: function() { stack = undefined; if ( !memory ) { self.disable(); } return this; },
locked:是否locked。 return !stack;
disable禁止全部操做
function aaa() { alert(1); } function bbb() { alert(2); } var cb = $.Callbacks('memory'); cb.add(aaa); cb.fire(); //1 cb.disable();//disable()后只能弹出1 由于禁止全部操做了,虽然有Memory cb.add(bbb);//不起做用了,此时list变为undefined了 cb.fire();//不起做用了
lock只是锁住数组
function aaa() { alert(1); } function bbb() { alert(2); } var cb = $.Callbacks('memory'); cb.add(aaa); cb.fire(); //1 2 cb.lock();//lock()只是把后续的fire()锁住,其余操做是锁不住的 cb.add(bbb); cb.fire();//不起做用了 此时list为[]
参考:
http://www.cnblogs.com/haogj/p/4473477.html
本文做者starof,因知识自己在变化,做者也在不断学习成长,文章内容也不定时更新,为避免误导读者,方便追根溯源,请诸位转载注明出处:http://www.cnblogs.com/starof/p/6885500.html有问题欢迎与我讨论,共同进步。