前言:
queue()方法和dequeue()方法是为 jQuery 的动画服务的,目的是为了允许一系列动画函数被异步调用,但不会阻塞程序。

所以这篇是为jQuery的动画解析做准备的。

一、$.queue()、$.dequeue() 和 $().queue()、$().dequeue() 的区别
(1)$().queue()$().dequeue()
这俩是jQuery.fn.extend()中的方法,也就是供开发者使用的方法,其内部会分别调用 $.queue()$.dequeue()方法。

//源码4686行jQuery.fn.extend( {  queue: function( type, data ) {    xxx    return jQuery.queue( this[ 0 ], type )  },  dequeue: function( type, data ) {    return jQuery.dequeue( this, type )  },})

(2)$.queue()$.dequeue()
这俩是jQuery.extend()中的方法,也就是 jQuery 内部使用的方法。

  //源码4594行  jQuery.extend( {    queue: function( elem, type, data ) {},    dequeue: function( elem, type ) {},  })

二、$().queue()
作用1:
作为setter,将function(){}存进特定队列中。

<div id="A" style="background-color: deeppink">这是A</div><script>  function a() {    console.log('a','a34')  }  function b() {    console.log('b','b37')  }  //将a、b方法存在类型为type的队列里  //jQuery.fn.queue 给jQuery对象$("A")  /*setter*/  $("#A").queue('type', a)  $("#A").queue('type', b)</script>

作用2:
作为getter,取出特定队列中function(){}的数组。

  /*getter*/  $("#A").queue('type') //[a,b]

源码:

 jQuery.fn.extend( {    //入队    //源码4663行    //'type', function(){xxx}    queue: function( type, data ) {      var setter = 2;      if ( typeof type !== "string" ) {        data = type;        type = "fx";        setter--;      }      //如果参数小于setter,就执行jQuery.queue()方法      /*这边就是getter*/      if ( arguments.length < setter ) {        //this[0] 目标DOM元素        //type "type"        //返回[function a(){xxx},function b(){xxx}]        return jQuery.queue( this[ 0 ], type );      }      //如果data是undefined就返回jQuery对象      return data === undefined ?        this :                this.each( function() {          /*这边是setter*/          var queue = jQuery.queue( this, type, data );          // Ensure a hooks for this queue          //确保该队列有一个hooks          //返回{empty:{          // 里面是jQuery.Callbacks方法          // 其中add方法被改写          // }}          jQuery._queueHooks( this, type );          /*专门为fx动画做处理*/          if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {            jQuery.dequeue( this, type );          }        } );      },})

解析:
不涉及 fx 动画的话,本质是调用的内部的jQuery.queue()方法
(1)如果不足两个参数的话,就调用jQuery. queue()get获取数据。
(2)如果大于等于两个参数的话,就调用jQuery. queue()set存数据,并且调用jQuery._queueHooks(),用来生成一个queueHooks对象或者返回当前值。
(3)如果是 fx 动画,并且队头没有inprogress锁的话,就执行jQuery.dequeue()方法。

三、jQuery._queueHooks()
作用:
如果目标元素的数据缓存(dataPriv)已存在名称type+queueHooksHooks话,则直接返回该Hooks,
否则返回有empty属性的jQuery.Callback()方法生成的对象:

其中的fire()方法用来清除队列。

源码:

    // Not public - generate a queueHooks object, or return the current one    //jQuery内部方法,生成一个queueHooks对象或者返回当前值    //目标元素,"type"    //源码4676行    _queueHooks: function( elem, type ) {      //typequeueHooks      var key = type + "queueHooks";      //如果dataPriv已存在名称typequeueHooks的Hooks话,则直接返回该Hooks      //否则返回有empty属性的jQuery.Callback()方法生成的对象      return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {        empty: jQuery.Callbacks( "once memory" ).add( function() {          dataPriv.remove( elem, [ type + "queue", key ] );        } )      } );    }

解析:
jQuery.Callbacks()方法会放到$.dequeue后讲解

四、jQuery.queue()
作用:
callback依次存入目标元素的queue中,或者取出queue

源码:

  jQuery.extend( {    //作用:目标元素可执行的任务队列    //源码4596行    //elem 目标元素    //$("#A"),"type",function(){xxx}    queue: function( elem, type, data ) {      var queue;      if ( elem ) {        //typequeue        type = ( type || "fx" ) + "queue";        //从数据缓存中获取typequeue队列,如果没有则为undefined        queue = dataPriv.get( elem, type );        // Speed up dequeue by getting out quickly if this is just a lookup        if ( data ) {          //如果queue不存在,或者data是Array的话          //就创建queue,queue=[data1,data2,...]          if ( !queue || Array.isArray( data ) ) {            queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );          }          //queue存在的话,就把data push进去          else {            queue.push( data );          }        }        //queue=[a,b]        return queue || [];      }    },})

解析:
(1)作为setter

  function a() {    console.log('a','a34')  }  $("#A").queue('type', a)

此时data存在,并且是第一次创建type='type'queue,所以使用dataPriv.access( elem, type, jQuery.makeArray( data ) )来创建queue,并把function a push 进queue中。

(2)作为getter

  $("#A").queue('type') //[a,b]

此时data不存在,直接从数据缓存中获取queue并返回。

注意:
jQuery.queue()始终返回queue数组,而$().queue()会返回 jQuery 对象或者是queue数组。

五、$().dequeue()
作用:
移出队头的函数并执行该callback

源码:

 jQuery.fn.extend( {    //出队    //移出队头的函数并执行它    //源码4717行    dequeue: function( type ) {      return this.each( function() {        jQuery.dequeue( this, type );      } );    },})

解析:
其实就是执行$.dequeue()函数。

六、jQuery.dequeue()
作用:
同五。

源码:

  jQuery.extend( {    //源码4624行    //目标元素,'type'    dequeue: function( elem, type ) {      //'type'      type = type || "fx";      //get,获取目标元素的队列      var queue = jQuery.queue( elem, type ),        //长度        startLength = queue.length,        //去除对首元素,并返回该元素        fn = queue.shift(),        //确保该队列有一个hooks        hooks = jQuery._queueHooks( elem, type ),        //next相当于dequeue的触发器        next = function() {          jQuery.dequeue( elem, type );        };      // If the fx queue is dequeued, always remove the progress sentinel      //如果fn='inprogress',说明是fx动画队列正在出队,就移除inprogress      if ( fn === "inprogress" ) {        fn = queue.shift();        startLength--;      }      if ( fn ) {        // Add a progress sentinel to prevent the fx queue from being        // automatically dequeued        //如果是fx动画队列的话,就添加inprogress标志,来防止自动出队执行        //意思应该是等上一个动画执行完毕后,再执行下一个动画        if ( type === "fx" ) {          queue.unshift( "inprogress" );        }        // Clear up the last queue stop function        //删除hooks的stop属性方法        delete hooks.stop;        //递归dequeue方法        fn.call( elem, next, hooks );      }      console.log(startLength,'startLength4669')      //如果队列是一个空数组,并且hooks存在的话,清除该队列      if ( !startLength && hooks ) {        console.log('aaaa','bbbb4671')        //进行队列清理        hooks.empty.fire();      }    },  })

解析:
(1)inprogress应该是一个锁,当 fx 动画执行动画 A 的时候,就加锁,当动画 A 执行完毕后,就解锁,再去运行下一个动画。

(2)注意下fn.call( elem, next, hooks ),保持fnthiselement的同时,给fn传的两个参数,分别为nexthooks,方便操作。

(3)当queue是空数组的时候,就触发hooks.emptyfire()方法,将queue清除。

七、jQuery.Callbacks()
作用:
jQuerycallbacks回调方法,返回一个object,里面包含 a、b、c 方法,在执行任意一个方法后,这个方法依旧返回 a、b、c 方法,所以jQuery.Callbacks()是链式调用的关键函数。

_queueHooks 中有用到该函数:

dataPriv.access( elem, key, {        empty: jQuery.Callbacks( "once memory" ).add( function() {          dataPriv.remove( elem, [ type + "queue", key ] );        } )      } );

源码:

  /*创建一个使用以下参数的callback列表 * Create a callback list using the following parameters: *  options:是一个可选的空格分开的参数,它可以改变callback列表的行为或形成新的option对象 *    options: an optional list of space-separated options that will change how *            the callback list behaves or a more traditional option object * 默认情况下一个回调列表会表现成一个event callback列表并且会触发多次 * By default a callback list will act like an event callback list and can be * "fired" multiple times. * option可选值: * Possible options: *  确保callback列表只会被触发一次,比如Deferred对象 *    once:            will ensure the callback list can only be fired once (like a Deferred) *  保持跟踪之前的values,并且会在list用最新的values触发后,调用该回调函数 *    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) *  //确保callback只会被添加一次 *    unique:            will ensure a callback can only be added once (no duplicate in the list) *  //当callbak返回false时打断调用 *    stopOnFalse:    interrupt callings when a callback returns false * */  //源码3407行  //callbacks回调对象,函数的统一管理  //once memory  jQuery.Callbacks = function( options ) {    // Convert options from String-formatted to Object-formatted if needed    // (we check in cache first)    options = typeof options === "string" ?      //options: {once:true,memory:true}      createOptions( options ) :      jQuery.extend( {}, options );    //用来知道list是否正被调用    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,      // Flag to prevent firing      locked,      // Actual callback list      list = [],      // Queue of execution data for repeatable lists      queue = [],      // Index of currently firing callback (modified by add/remove as needed)      firingIndex = -1,      //触发list中的回调函数      // Fire callbacks      fire = function() {        //true        // Enforce single-firing        //'once memory'中的'once'只允许触发一次        locked = locked || options.once;        // Execute callbacks for all pending executions,        // respecting firingIndex overrides and runtime changes        fired = firing = true;        for ( ; queue.length; firingIndex = -1 ) {          //从queue移除第一个元素,并返回该元素          memory = queue.shift();          while ( ++firingIndex < list.length ) {            // Run callback and check for early termination            //memory=[document, Array(1)]            //memory[0]是document            //意思就是让document去执行add()方法中添加的callback函数            if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&              options.stopOnFalse ) {              // Jump to end and forget the data so .add doesn't re-fire              firingIndex = list.length;              memory = false;            }          }        }        // Forget the data if we're done with it        if ( !options.memory ) {          memory = false;        }        firing = false;        // Clean up if we're done firing for good        //如果once:true,清空list数组        if ( locked ) {          // Keep an empty list if we have data for future add calls          if ( memory ) {            list = [];            // Otherwise, this object is spent          } else {            list = "";          }        }      },      // Actual Callbacks object      self = {        //添加一个回调函数或者是一个回调函数的集合        // Add a callback or a collection of callbacks to the list        add: function() {          if ( list ) {            // If we have memory from a past run, we should fire after adding            if ( memory && !firing ) {              firingIndex = list.length - 1;              queue.push( memory );            }            //闭包            //将arguments作为参数即args传入闭包的add方法中            ( function add( args ) {              //args[0]即function(){dataPriv.remove( elem, [ type + "queue", key ] ) }              jQuery.each( args, function( _, arg ) {                if ( isFunction( arg ) ) {                  //如果self对象没有该方法,将其push进list中                  if ( !options.unique || !self.has( arg ) ) {                    list.push( arg );                  }                } else if ( arg && arg.length && toType( arg ) !== "string" ) {                  // Inspect recursively                  add( arg );                }              } );            } )( arguments );            //undefined undefined            if ( memory && !firing ) {              fire();            }          }          //this即self对象          //也就说在调用self对象内的方法后会返回self对象本身          return this;        },        // Remove a callback from the list        remove: function() {          jQuery.each( arguments, function( _, arg ) {            var index;            while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {              list.splice( index, 1 );              // Handle firing indexes              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.length > 0;        },        // Remove all callbacks from the list        empty: function() {          if ( list ) {            list = [];          }          return this;        },        // Disable .fire and .add        // Abort any current/pending executions        // Clear all callbacks and values        disable: function() {          locked = queue = [];          list = memory = "";          return this;        },        disabled: function() {          return !list;        },        // Disable .fire        // Also disable .add unless we have memory (since it would have no effect)        // Abort any pending executions        lock: function() {          locked = queue = [];          if ( !memory && !firing ) {            list = memory = "";          }          return this;        },        locked: function() {          return !!locked;        },        // Call all callbacks with the given context and arguments        fireWith: function( context, args ) {          if ( !locked ) {            args = args || [];            args = [ context, args.slice ? args.slice() : args ];            queue.push( args );            if ( !firing ) {              fire();            }          }          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;        }      };    console.log(queue,'queue3614')    return self;  };

解析:
主要看add()fire()方法
(1)self.add()
注意里面的闭包函数,使用闭包的目的是冻结args的值,这样可以避免异步调用造成的值得改变。

add()方法就是将function() {dataPriv.remove( elem, [ type + "queue", key ] );}push 进 list 数组中,以供fire()来调用 list 中的callback。

注意最后返回的是this,即self对象,也就说在调用self对象内的方法后会返回self对象本身,而self内部又含有add()、fire()等方法,通过jQuery.Callbacks传入的参数options来控制能否调用,及调用的次数。

(2)self.fire()
作用是触发 list 中的回调函数,onece memoryonce表示只让fire()触发一次后,就需要清理 list,memory表示是将 list 清空成空数组还是空字符。

八、createOptions()
作用:
将特定格式的string(空格分开),转化为特定格式的object({xxx:true,xxx:true,...} .

源码:

  //将特定格式的string(空格分开),转化为特定格式的object({xxx:true,xxx:true,...})// Convert String-formatted options into Object-formatted ones  //源码3377行  //'once memory' —> {once:true,memory:true}  function createOptions( options ) {    var object = {};    jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {      object[ flag ] = true;    } );    return object;  }

解析:
将以空格连接的字符串,以空格拆开,并作为 object 的key,其 value 为 true

比如:
"once memory" => {once:true,memory:true,}


(完)