问题1:当我们回调队列中的函数的时候会传给他什么参数,他的上下文是什么
/*通过_queueHooks我们知道,他其中封装了Callbacks对象,他可以提前使得我们清除元素上面的队列集合,同时once和memory告诉我们调用fire以后,就会使得Callbacks中封装的集合变成[],所以多次调用fire是没有意义的*/ _queueHooks: function( elem, type ) { var key = type + "queueHooks"; return jQuery._data( elem, key ) || jQuery._data( elem, key, { empty: jQuery.Callbacks("once memory").add(function() { jQuery._removeData( elem, type + "queue" );//在queue方法中通过jQuery._data( elem,(type || "fx")+ "queue")保存 jQuery._removeData( elem, key );//promise方法中jQuery._data( elements[ i ], type + "queueHooks" ) }) }); } hooks = jQuery._queueHooks( elem, type ); next = function() { jQuery.dequeue( elem, type ); }; fn.call( elem, next, hooks );note:可以看到,在我们的函数队列执行的时候上下文为调用对象的DOM对象,第一个参数是next可以调用下一个函数,也就是执行dequeue操作;h ooks可以让我们提前终止后续函数的调用。通过调用hooks.empty.fire来完成!
if ( type === "fx" && queue[0] !== "inprogress" ) { jQuery.dequeue( this, type ); }
note:这句代码告诉我们如果我们指定队列名称或者队列名称为fx时候就会执行一次dequeue操作,也就是调用函数队列中的第一个调用。调用后,该函数从回调队列中移除。
问题3:如果调用queue方法时候只是传入了一个字符串参数,那么为什么只会返回第一个调用对象,也就是this[0]的函数队列
if ( arguments.length < setter ) { return jQuery.queue( this[0], type ); }note:当只是传入一个参数的时候,那么arguments.length为1,但是setter默认是2,于是只是返回第一个调用对象的函数队列
$("div").queue(undefined)源码分析之queue方法:
queue: function( type, data ) { var setter = 2;//默认参数是两个 if ( typeof type !== "string" ) { data = type; type = "fx"; setter--; }//如果只是传入一个字符串,那么表示获取调用对象第1个DOM的队列 if ( arguments.length < setter ) { return jQuery.queue( this[0], type ); } return data === undefined ?//如果传入undefined那么返回调用对象 this : this.each(function() {//每一个对象都保存一个队列,队列通过_data方法保存到DOM对象作为jQuery内部数据 var queue = jQuery.queue( this, type, data ); // ensure a hooks for this queue jQuery._queueHooks( this, type );//同时每一个对象都维持自己的Callbacks对象,通过这个对象提前结束这个DOM对象的所有回调 if ( type === "fx" && queue[0] !== "inprogress" ) { jQuery.dequeue( this, type ); }//如果没有指定队列名,或者名字为fix,那么调用每一个DOM对象的第一个回调函数,并且从队列中移除! }); }note: Callbacks对象,可以让每一个DOM对象维持的这个函数队列提前结束,如下面的例子:
$("#div1").queue("fx",[function(next,hooks){ //next表示执行id为#div1这个元素维持的函数队列的下一个函数,hooks表示上面的jQuery._queueHooks。如果执行hooks.empty.fire()就能提前结束动画 }])因为实例queue方法调用了jQuery.queue方法,我们看看他的源码:
queue: function( elem, type, data ) { var queue; if ( elem ) { //默认为fixqueue,这里你就明白了为什么hooks的empty方法要清除type+"queue"的内部数据 type = ( type || "fx" ) + "queue"; //第一次默认是undefined queue = jQuery._data( elem, type );//保存到fixqueue中作为内部数据! // Speed up dequeue by getting out quickly if this is just a lookup if ( data ) { if ( !queue || jQuery.isArray(data) ) { //如果没有获取到_data方法保存的数据,那么我们把函数队列通过_data方法保存到DOM上面!如果已经存在,那么直接插入到原来的集合中! queue = jQuery._data( elem, type, jQuery.makeArray(data) ); } else { queue.push( data ); } } return queue || []; } }note:这个方法是可以获取也是可以保存的,如果没有data,也就是调用queue时候没有传入第二个参数,那么直接表示获取函数队列, 否则就是把函数队列保存到DOM对象的内部数据上!
dequeue: function( type ) { return this.each(function() { jQuery.dequeue( this, type ); }); }note:如果是$("div")调用dequeue方法,那么会为每一个DOM对象调用一次dequeue方法。 注意:调用一次dequeue方法,那么该回调函数就会从队列中移除!我们来看看jQuery.dequeue方法:
dequeue: function( elem, type ) { type = type || "fx";//首先获取到该DOM上面的所有的函数队列 var queue = jQuery.queue( elem, type ), startLength = queue.length, fn = queue.shift(), hooks = jQuery._queueHooks( elem, type ), next = function() { jQuery.dequeue( elem, type ); }; // If the fx queue is dequeued, always remove the progress sentinel if ( fn === "inprogress" ) {//每次显示调用之前我们必须手动移除上一次添加的字符串"inprogress" fn = queue.shift(); startLength--; } if ( fn ) { // Add a progress sentinel to prevent the fx queue from being // automatically dequeued if ( type === "fx" ) {//每次显示调用dequeue以后,我们就会往回调队列中放入一个字符串"inprogress" queue.unshift( "inprogress" ); } // clear up the last queue stop function delete hooks.stop; fn.call( elem, next, hooks ); } if ( !startLength && hooks ) {//如果startLength为0以后,我们手动销毁Callbacks中封装的lists数组! hooks.empty.fire(); } }note:每次调用dequeue方法都会把startLength自减,注意:这里是shift,自减的同时把它从回调队列中移除!如果自减后为0,那么就会hooks.empty.fire清除所有的回调队列,这时候再次调用dequeue已经没有任何意义了,因为Callbacks内部的list以及是空数组了!
clearQueue: function( type ) { return this.queue( type || "fx", [] ); }
clearQueue()
函数用于
清空每个匹配元素的指定队列中所有尚未执行的项。底层通过jQuery._data把原来保存的还没有执行的函数数组变成空数组!
(1)队列的方法还是通过数组配合shift和unshift来完成的,首先为所有的调用对象内部通过jQuery._data方法保存调用函数队列,但是如果没有指定队列名称或者队列名称是"fx"那么没有调用dequeue就会执行队列中第一个函数。同时我们注意,当调用dequeue的次数已经多于本身函数队列的长度的时候,多次调用是没有意义的,因为这时候Callbacks对象中的list已经是空了,所以调用fire函数是多余的,不会有任何反应。
(2)学习该方法要特别注意每一个函数队列中的函数回调的时候上下文都是DOM对象,第一个参数是next表示可以调用dequeue方法,而hooks可以帮助我们在程序中手动停止剩下的函数的调用,而不依赖于clearQueue方法!