资讯专栏INFORMATION COLUMN

jQuery源码解析之$.queue()、$.dequeue()和jQuery.Callbacks(

itvincent / 2918人阅读

摘要:作为此时不存在,直接从数据缓存中获取并返回。作用是触发中的回调函数,的表示只让触发一次后,就需要清理,表示是将清空成空数组还是空字符。

前言:
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(){}存进特定队列中。

这是A

作用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,}

(完)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104920.html

相关文章

  • jQuery源码解析$().animate()(上)

    摘要:前言需要先看源码解析之和一举例的宽度先变成,再变成,最后变成这是在异步调用中,进行同步调用动画是异步的就是连续调用二作用通过样式将元素从一个状态改变为另一个状态源码之前有说过是的方法源码行是否是空对象,方法执行单个动画的封装的本质是执行 showImg(https://segmentfault.com/img/remote/1460000019594521); 前言:需要先看 jQue...

    Batkid 评论0 收藏0
  • FE.SRC-逐行分析jQuery2.0.3源码-完整笔记

    摘要:根据项目选型决定是否开启。为了压缩,可维护为了支持从而使用代替变量存储防冲突会用到,形如版本号声明最终调用的是这个原型实际上。功能检测统一兼容性问题。 概览 (function (){ (21 , 94) 定义了一些变量和函数 jQuery=function(); (96 , 293) 给jQuery对象添加一些方法和属性; (285 , 347) ...

    Lin_R 评论0 收藏0
  • FE.SRC-逐行分析jQuery2.0.3源码-完整笔记

    摘要:根据项目选型决定是否开启。为了压缩,可维护为了支持从而使用代替变量存储防冲突会用到,形如版本号声明最终调用的是这个原型实际上。功能检测统一兼容性问题。 概览 (function (){ (21 , 94) 定义了一些变量和函数 jQuery=function(); (96 , 293) 给jQuery对象添加一些方法和属性; (285 , 347) ...

    褰辩话 评论0 收藏0

发表评论

0条评论

itvincent

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<