zoukankan      html  css  js  c++  java
  • jQuery源码分析系列:队列操作

    Queue队列:

    队列是一种特殊的线性表,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队)。队列的特点是先进先出(FIFO-first in first out),即最先插入的元素最先被删除。

    jQuery提供了jQuery.queue/dequeue和jQuery.fn.queue/dequeue,实现对队列的入队、出队操作。不同于队列定义的是,jQuery.queue和jQuery.fn.queue不仅执行出队操作,返回队头元素,还会自动执行返回的队头元素。

     

    queue是用来维护函数队列的。比较常用的是queue(queueName, callback);其中queueName缺省是fn,即标准函数队列。 每个Element可以拥有多个队列,但是基本上都只使用到一个,即默认的fn队列。队列允许一系列函数被异步地调用而不会阻塞程序。 例如:$("#foo").slideUp().fadeIn();其实这个就是我们大家常用的链式调用,实际上这是一个Queue。所以队列和Deferred地位类似, 是一个内部使用的基础设施。当slideUp运行时,fadeIn被放到fx队列中,当slideUp完成后,从队列中被取出运行。queue函数允许 直接操作这个链式调用的行为。同时,queue可以指定队列名称获得其他能力,而不局限于fx队列

     1 // 一般用法:
     2 $("#foo").slideUp(function() {
     3     alert("Animation complete.");
     4 });
     5 // 相当于:
     6 $("#foo").slideUp();  // 不提供回调,只是触发事件
     7 $("#foo").queue(function() {  // 把回调函数加入
     8     alert("Animation complete.");
     9     $(this).dequeue();  // 必须从队列中取出,那么队列中的下一个函数就有机会被调用
    10 });

     调用时,如果不传入队列名,则默认为fx(标准动画)
                1>>队列用数组实现,入队直接调用数组对象的方法 push入队
                2>>入队必须是函数,或函数数组
                3>>所有队列名加上queue后缀,表示这是一个队列
                4>>如果传入的是数组,覆盖现有队列
                5>>如果不是数组,则直接入队

    1.  调用jQuery. dequeue出队时,会先调用jQuery.queue取得整个队列,因为队列用数组实现,可以调用数组的shift方法取出第一个元素并执行
    2.  执行第一个元素时采用function.call( context, args ),由此可以看出jQuery队列只支持函数(这么说不完全准确,fx动画是个特例,
         会在队列头端插入哨兵inprogress,类型为字符串)
    3.  出队的元素会自动执行,无论这个元素是不是函数,如果不是函数此时就会抛出异常(这个异常并没有处理)
    4.  如果队列变成空队列,则用关键delete删除jQuery.cache中type对应的属性

    示例:
    1.     先入队3个弹窗函数,分别弹出1、2、3

    1         $('body').queue( 'test', function(){ alert(1); } )
    2         $('body').queue( 'test', function(){ alert(2); } )
    3         $('body').queue( 'test', function(){ alert(3); } )


    2.     查看jQuery.data为body分配的唯一id(为什么要查看body的唯一id,请参考数据缓存的解析)

    1 $.expando : "jQuery161017518149125935123"
    2 
    3 command: >>> $('body')[0][$.expando]

    $('body')[0]["jQuery161017518149125935123"]
    $.expando有三部分构成:字符串"jQuery" + 版本号jQuery.fn.jquery + 随机数Math.random(),因此每次加载页面后都不相同。

    3.     查看jQuery.cache对属性5对应的数据,格式化如下:
     

     1        {
     2             "1" : { ... },
     3             "2" : { ... },
     4             "3" : { ... },
     5             "4" : { ... },
     6             "5" : {
     7                 "jQuery161017518149125935123" : {
     8                     "testqueue" : [(function () {alert(1);}),(function () {alert(2);}),(function () {alert(3);})]
     9                 }
    10             }
    11         }

    内部数据存储在$.expando属性("jQuery161017518149125935123")中,这点区别于普通数据

    4.连续3次调用出队$('body').dequeue( 'test' ),
            每次调用dequeue后用$('body').queue('test').length检查队列长度  控制台命令

    1 $('body').dequeue( 'test' );
    2 console.log($('body').dequeue( 'test' ).length);//2
    3 
    4 $('body').dequeue( 'test' );
    5 console.log($('body').dequeue( 'test' ).length);//1
    6 
    7 $('body').dequeue( 'test' );
    8 console.log($('body').dequeue( 'test' ).length);//0

      调用出队函数dequeue后,入队的函数按照先进先出的顺序,依次被执行


    5. 最后看看全部出队后,jQuery.cache中的状态
           

    1 $.cache[5][$.expando]['testqueue'] //undefined  

    可以看到,testqueue属性已经从body的缓存中移除
           

    jQuery.queue(element,[queueNmae]):返回在指定的元素element上将要执行的函数队列
    jQuery.queue(element,queueName,newQueue or callback):修改在指定的元素element上将要执行的函数队列
    使用jQuery.queue()添加函数后,最后要调用jQuery.queue(),使得下一个函数能线性执行

    调用jQuery.data 存储为内部数据(pvt 为true)

    1 queue:function(elem,type,data){
    2   "jQuery161017518149125935123" : {
    3       "testqueue" : [(function () {alert(1);}),(function () {alert(2);}),(function () {alert(3);})]
    4   }

    queue内部使用data或者JavaScript数组API来保存数据。其中操作数组的push和shift天生就是一组队列API。而data可以用来保存任意数据。

    源码分析:

      1 jQuery.extend({
      2         //计数器 用在animate中
      3         _mark:function(elem,type){
      4             if(elem){
      5                 type = (type || "fx") + "mark";
      6                 //取出数据加1  存储在内部对象上
      7                 jQuery.data(elem,type,(jQuery.data(elem,type,undefined,true) || 0)+1,true);
      8             }
      9         },
     10         //用在animate中 减一
     11         _unmark:function(force,elem,type){
     12             if(force !==true){
     13                 type = elem;
     14                 elem = force;
     15                 force = false;
     16             }
     17             if(elem){
     18                 type = type || "fx";
     19                 var key = type + "mark",
     20                 count = force ? 0 :((jQuery.data(elem,key,undefined,true) || 1) -1);
     21                 if(count){
     22                     jQuery.data(elem,key,count,true);
     23                 }else{
     24                     jQuery.removeData(elem,key,true);
     25                     handleQueueMarkDefer(elem,type,"mark");
     26                 }
     27             }
     28         },
     29             //elem必须存在
     30             if(elem){
     31                 //默认fn:为type或者fx   type相当于例子中的test
     32                 type = (type || "fx") + "queue";//属性名加上queue
     33             }
     34             //取出队列  data 内部API:data(elem,key,value,pvt);
     35             //这里不传入value  只是取队列。
     36             var q = jQuery.data(elem,type,undefined,true);
     37             //如果data存在,才会进行后边的转换数组 入队等操作
     38             if(data){
     39                 //队列不存在或者data是数组  可以makeArray转换
     40                 if(!q || jQuery.isArray(data){
     41                     //数组实现队列 type的属性值为jQuery.makeArray(data)数组
     42                     q = jQuery.data(elem,type,jQuery.makeArray(data),true);
     43                 }else{
     44                     //如果不是数组,则直接入队
     45                     q.push(data);
     46                 }
     47             }
     48             //返回队列 即入队同时返回整个队列
     49             //简洁实用的避免空引用的技巧
     50             return q || [];
     51         },
     52         //出队并执行  调用jQuery.queue取的整个队列  再调用shift取出第一个元素来执行
     53         dequeue:function(elem,type){
     54             type = type || "fx";//默认为fx   入队被改为fxqueue了?
     55             //得到这个队列  执行这句 var q = jQuery.data(elem,type,undefined,true);
     56             var queue = jQuery.queue(elem,type),//取出队列
     57 
     58             fn = queue.shift(),//取出第一个  shift():用于把数组的第一个元素从其中删除,并返回第一个元素的值
     59             defer;
     60             //"inprogress"岗哨  如果第一个元素时岗哨
     61             if(fn === "inprogress"){//如果取出来的fn是一个正在执行中的标准动画fx,抛弃执行哨兵(inprogress)  重新取
     62                 fn = queue.shift();//这里是取第二个作为fn的值,因为第一个取到的是正在执行的fx
     63             }
     64 
     65             if(fn){
     66                 //如果是标准动画,则在队列头部增加处理中哨兵属性,阻止fx自动执行
     67                 if(type === "fx"){
     68                     //在队列头部增加哨兵 inprogress   unshift():可向数组的开头添加一个或更多元素
     69                     queue.unshift("inprogress");//标准动画 在第一个位置插入inprogress
     70                 }
     71                 //fn函数
     72                 fn.call(elem,function(){
     73                     //这个回调函数不会自动执行  没有return
     74                     jQuery.dequeue(elem,type);
     75                 });
     76             }
     77             if(!queue.length){//删除缓存的数据
     78                 jQuery.removeData(elem,type + "queue",true);//内部使用delete删除type对应的空数组
     79                 handleQueueMarkDefer(elem,type,"queue");
     80             }
     81         }
     82     });
     83 jQuery.fn.extend({
     84         //queue([queueName])返回指定元素上将要执行的函数队列
     85         //queue([queueName],callback)修改指定元素上将要执行的函数队列
     86         queue:function(type,data){
     87             //只传了一个非字符串的参数 则默认为动画fx
     88             if(typeof type !== "string"){
     89                 data = type;
     90                 type = "fx";
     91             }
     92             //data等于undefined  认为是取队列
     93             if(data === undefined){//取值
     94                 return jQuery.queue(this[0],type);
     95             }
     96             //如果传入了data  在每一个匹配的元素上执行入队操作
     97             return this.each(function(){//修改
     98                 var queue = jQuery.queue(this,type,data);
     99                 //如果动画执行完毕(且不是inprogress)从队列头部移除
    100                 if(type === "fx" && queue[0] !== "inprogree"){
    101                     jQuery.dequeue(this,type);//移除
    102                 }
    103             });
    104         },
    105         //调用jQuery.dequeue出队
    106         dequeue:function(type){
    107             return this.each(function(){
    108                 jQuery.dequeue(this,type);
    109             }
    110         },
    111         //延迟执行队列中未执行的函数,通过在队列中插入一个时延出队的函数来实现
    112         delay:function(time,type){
    113             time = jQuery.fx ? jQuery.fx.speeds[time] || time :time;
    114             type = type || "fx";
    115             return this.queue(type,function(){
    116                 var elem = this;
    117                 setTimeout(function(){
    118                     jQuery.dequeue(elem,type);
    119                 },time);
    120             }
    121         },
    122         //清空队列,通过将第二个参数设为空数组[]
    123         clearQueue:function(type){
    124             return this.queue(type || "fx",[]);
    125         },
    126         //返回一个只读视图,当队列中指定类型的函数执行完毕后
    127         promise:function(type,object){
    128             //type不是字符串,值object是type
    129             if(typeof type !== "string"){
    130                 object = type;
    131                 type = undefined;
    132             }
    133             type = type || "fx"; //取type的值  或者是fx
    134             var defer = jQuery.Deferred(),
    135                 elements = this,
    136                 i = elements.length,
    137                 count =1,
    138                 deferDataKey = type + "defer",
    139                 queueDataKey = type + "queue",
    140                 markDataKey = type + "mark",
    141                 tmp;
    142             function resolve(){
    143                 if(!(--count)){//如果count=0  执行队列回调函数函数使用指定上下文,context :elements,args:elements
    144                     defer.resolveWith(elements,[elements]);
    145                 }
    146             }
    147                while( i-- ) {
    148                    //延迟队列   或者执行队列 或者标记队列?
    149                 if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
    150                     ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
    151                     jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
    152                     jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) {
    153                     count++;
    154                     tmp.done( resolve );//
    155                }
    156             }
    157             resolve();
    158                return defer.promise();
    159         }
    160     });
  • 相关阅读:
    带有时间间隔的dp
    单调队列优化dp(捡垃圾的机器人)
    实现技巧
    树形dp(灯与街道)
    括号匹配(数组链表模拟)
    二分,求直线上覆盖所有点的最短时间
    可持久化链表(链式前向星)
    二分图匹配模板题
    网络流,设备、插头和转接器建图(简单map的应用)
    第七周助教总结
  • 原文地址:https://www.cnblogs.com/colorstory/p/2612762.html
Copyright © 2011-2022 走看看