jQuery 源码分析(十一) 队列模块 Queue详解

队列是常用的数据结构之一,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队)。特点是先进先出,最先插入的元素最先被删除。

在jQuery内部,队列模块为动画模块提供基础功能,负责存储动画函数、自动出队并执行动画函数,同时还要确保动画函数的顺序执行。

jQuery的静态方法含有如下API:

  • $.queue(elem,type,data) ;返回或修改匹配元素关联的队列,返回最新的队列,参数如下:

                 elem   ;DOM元素或JavaScript对象

                type   ;队列名称,默认是标准动画fx

                data  ;需要设置的队列函数,可以是空(返回队列)、函数(加入队列)或函数数组(替换队列)

  • $.dequeue(elem,type)   ;用于出队并执行匹配元素关联的函数队列中的下一个元素

                elem  ;DOM元素或JavaScript对象

                type  ;是队列名称,默认为动画队列fx

writer by:大沙漠 QQ:22969969

jQuery/$ 实例方法(可以通过jQuery实例调用的):

  • queue(type,data)        ;返回第一个匹配元素的函数队列,或修改所有匹配的元素关联的函数队列,参数如下:

                type  ;队列名称

                data  ;data是可选的函数或函数数组,参数同$.queue()的第三个参数

  • dequeue(type)       ;出队并执行所有匹配元素关联的函数队列中的下一个函数
  • delay(time,type)          ;延迟函数出队执行。通过调用.queue(type,data)向关联的函数队列中插入一个新的函数,在函数内通过setTimeout()延迟下一个函数的出队时间。
  • clearQueue(type)          ;移除匹配元素关联的函数队列中的所有未被执行的函数,内部代码就一句:return this.queue( type || "fx", [] );

举个栗子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
</head>
<body>
    <p>123</p>
    <script>
        function f1(){console.log(‘f1触发‘);}    //定义两个测试函数
        function f2(){console.log(‘f2触发‘)};

        $(‘p‘).queue(‘test‘,f1);                 //将f1入队,队列名称为test
        $(‘p‘).dequeue(‘test‘);                //将匹配元素的名称为test的函数列表出队并执行
        $(‘p‘).queue(f2);                    //将f2放入p匹配元素的队列中,默认为动画队列,会自动执行。
    </script>
</body>
</html>

输出如下:

源码分析



jQuery的队列是基于数据缓存模块$.data来实现的,当调用$.queue()时回把函数列表以队列名+‘queue‘为属性,保存在对应的DOM元素的内部缓存对象上,例如:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
</head>
<body>
    <p>123</p>
    <script>
        function f1(){console.log(‘f1触发‘);}
        $(‘p‘).queue(‘test‘,f1);
    </script>
</body>
</html>

我们可以在控制台里直接从$.cache里获取对应的属性,如下:

document.getElementsByTagName(‘p‘)[0][$.expando]就是例子里p元素节点对象的$.expando属性,该属性的值会作为$.cache的某个属性,存储着对应这个p元素的数据缓存对象(这是数据缓存模块的内容)

$.queue和$.dequeue的实现如下:

jQuery.extend({
    /*略*/
    queue: function( elem, type, data ) {            //返回或修改匹配元素关联的队列。elem是DOM元素或JavaScript对象,type是队列名称,data是可选的函数或函数数组
        var q;
        if ( elem ) {
            type = ( type || "fx" ) + "queue";                //修正参数type,默认为动画队列fx,在参数type后面加上queue表示这是一个队列
            q = jQuery._data( elem, type );                    //取出参数type对应的队列 如果之前有数据则返回该数组 否则 q等于undefined

            // Speed up dequeue by getting out quickly if this is just a lookup
            if ( data ) {                                    //如果传入了data参数
                if ( !q || jQuery.isArray(data) ) {                //如果type队列不存在,或者type队列存在且data是一个数组
                    q = jQuery._data( elem, type, jQuery.makeArray(data) );    //调用jQuery.makeArray把参数data转换为数组并替换队列
                } else {
                    q.push( data );                                //队列存在且data不是一个数组则调用数组push方法把参数data放入队列
                }
            }
            return q || [];
        }
    },

    dequeue: function( elem, type ) {                //用于出队并执行匹配元素关联的函数队列中的下一个元素
        type = type || "fx";                            //修正参数type,默认是"fx";

        var queue = jQuery.queue( elem, type ),            //获取elem元素的type队列
            fn = queue.shift(),                            //调用shift方法取出队列第一个函数
            hooks = {};                                    //存放出队的函数在执行时的数据

        // If the fx queue is dequeued, always remove the progress sentinel
        if ( fn === "inprogress" ) {                    //如果出队的是占位符"inprogress",则丢弃再从队列头部出一个,只有动画队列会设置占位符"inprogress"
            fn = queue.shift();
        }

        if ( fn ) {
            // Add a progress sentinel to prevent the fx queue from being
            // automatically dequeued
            if ( type === "fx" ) {                        //如果是动画队列
                queue.unshift( "inprogress" );                //则在队列开头添加一个占位符"inprogress",表示动画函数正在执行当中
            }

            jQuery._data( elem, type + ".run", hooks );    //设置内部数据type+".run",表示参数type对应的队列正在执行,值是hooks,它会被作为第二个参数传递给出队的函数
            fn.call( elem, function() {
                jQuery.dequeue( elem, type );
            }, hooks );                                    //调用函数方法call执行出队的函数,elem是函数执行的上下文,即关键词this指向的对象;第二个参数是封装了jQuery.dequeue( elem, type )的函数,不会自动执行,需要在出队的函数返回前手动调用next()
        }

        if ( !queue.length ) {                            //如果参数type对应的队列在出队后成为空队列,即所有函数都已经出队并执行
            jQuery.removeData( elem, type + "queue " + type + ".run", true );        //调用jQuery.removeData()方法移除参数type对应的数据缓存对象
            handleQueueMarkDefer( elem, type, "queue" );                            //检查匹配元素关联的队列(type+"queue")和计数器(type+"mark")是否完成,如果完成则触发方法.promise()中的计数器
        }
    }
});

type默认等于fx,jQuery的动画效果也是基于Queue实现的,这个fx默认就是动画效果,对于jQuery/$ 实例方法来说,它是调用jQuery的静态方法来实现的,如下:

jQuery.fn.extend({
    queue: function( type, data ) {            //返回第一个匹配元素的函数队列,或修改所有匹配的元素关联的函数队列。type是队列名称,默认是fx。data参数等同于jQuery.queue中的data参数
        if ( typeof type !== "string" ) {        //修正参数    当传入的格式是queue()或queue(data) (注:data可以是函数或函数数组)时
            data = type;
            type = "fx";
        }

        if ( data === undefined ) {                //如果没有传入data参数,
            return jQuery.queue( this[0], type );    //则调用jQuery.queue()返回第一个匹配元素上参数type对应的队列
        }
        return this.each(function() {            //如果传入了data参数
            var queue = jQuery.queue( this, type, data );        //为每一个匹配元素调用jQuery.queue( this, type, data ),把参数(函数)入队,或者用参数data(函数数组)替换队列。

            if ( type === "fx" && queue[0] !== "inprogress" ) {    //对于动画队列fx,且没有动画函数正在执行,则立即出队并执行动画函数。
                jQuery.dequeue( this, type );
            }
        });
    },
    dequeue: function( type ) {                    //出队并执行匹配元素关联的函数队列中的下一个函数
        return this.each(function() {
            jQuery.dequeue( this, type );
        });
    },
    /*略*/
})

原文地址:https://www.cnblogs.com/greatdesert/p/11670591.html

时间: 2024-08-26 07:45:00

jQuery 源码分析(十一) 队列模块 Queue详解的相关文章

jQuery 源码分析(十八) ready事件详解

ready事件是当DOM文档树加载完成后执行一个函数(不包含图片,css等),因此它的触发要早于load事件.用法: $(document).ready(fun) ;fun是一个函数,这样当DOM树加载完毕后就会执行该匿名函数了 ready有一个简写,可以直接传入$(fun)即可,这是因为在jQuey内部也定义了一个$(document)的jQuery对象,和我们在上面的写法是一样的 ready事件和window的onload区别: ready事件 ;等dom树载完毕后就可以执行 onload事

ZRender源码分析5:Shape绘图详解

回顾 上一篇说到:ZRender源码分析4:Painter(View层)-中,这次,来补充一下具体的shape 关于热区的边框 以圆形为例: document.addEventListener('DOMContentLoaded', function () { var canvasDom = document.getElementById('canvasId'), context = canvasDom.getContext('2d'); context.lineWidth = 50; cont

Android源码分析--Handler和Looper机制详解

在Android系统中的应用程序,与Java的应用程序相同,都是靠消息驱动,简单的说就是:有一个消息队列,我们可以不断的向这个消息队列中添加消息,并从中取出消息,处理消息.Android中与此工作相关的主要是由Handler,Looper以及Message来完成. Looper类:为一个线程运行着一个消息循环,内部有一个消息队列,每一个线程只允许最多存在一个Looper: Handler类:允许你向一个线程的消息队列中发送消息,处理消息: Message类:消息类. 使用样例 首先,我们通过一个

Vue-Router 源码分析(七) VueRouter.push()的详解

通过VueRouter实例的push()操作,可以进行路由跳转,对于<router-link/>组件来说,它绑定的是click事件,最后也是通过执行push()方法来进行路由跳转的. 对于push()方法来说,一共可以传入三种形式的参数: 字符串形式,值为路劲 含有name的对象形式,可以搭配params属性传递参数 含有path的对象形式 举个栗子: <div id="app"> <button @click="test">测试

jquery源码分析(七)——事件模块 event(二)

上一章节探讨了事件的一些概念,接下来看下jQuery的事件模块. jQuery对事件的绑定分别有几个API:.bind()/.live()/.delegate()/.on()/click(), 不管是用什么方式绑定,归根到底还是用addEventListener/attachEvent(IE)处理的,正如jQuery的选择器一样不管如何匹配最终还是使用浏览器提供的几个接口处理. 那么现在就有个疑问,事件为什么还要区分那么多不同的处理方案? 这里就要涉及到之前提到的 DOM 事件处理模型了,捕获与

ZRender源码分析6:Shape对象详解之路径

开始 说到这里,就不得不提SVG的路径操作了,因为ZRender完全的模拟了SVG原生的path元素的用法,很是强大. 关于SVG的Path,请看这里: Path (英文版) 或者 [MDN]SVG教程(5) 路径 [译] (中文版), 很明显的是canvas中的路径没有SVG的用着舒服,那到底ZRender是如何实现的呢,让我给你娓娓道来(不过要想继续进行下去,上面的SVG的PATH必须了解.). 示例 打开API,shape.path,可以看到,path的配置有MLHVCSQTZ等字母组成的

mybatis 源码分析(五)Interceptor 详解

本篇博客将主要讲解 mybatis 插件的主要流程,其中主要包括动态代理和责任链的使用: 一.mybatis 拦截器主体结构 在编写 mybatis 插件的时候,首先要实现 Interceptor 接口,然后在 mybatis-conf.xml 中添加插件, <configuration> <plugins> <plugin interceptor="***.interceptor1"/> <plugin interceptor="*

jQuery源码分析(九) 异步队列模块 Deferred 详解

deferred对象就是jQuery的回调函数解决方案,它解决了如何处理耗时操作的问题,比如一些Ajax操作,动画操作等.(P.s:紧跟上一节:https://www.cnblogs.com/greatdesert/p/11433365.html的内容) 异步队列有三种状态:待定(pending).成功(resolved)和失败(rejected),初始时处于pending状态 我们可以使用jQuery.Deferred创建一个异步队列,返回一个对象,该对象含有如下操作: done(fn/arr

jQuery源码分析系列(38) : 队列操作

Queue队列,如同data数据缓存与Deferred异步模型一样,都是jQuery库的内部实现的基础设施 Queue队列是animate动画依赖的基础设施,整个jQuery中队列仅供给动画使用 Queue队列 队列是一种特殊的线性表,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队).队列的特点是先进先出(FIFO-first in first out),即最先插入的元素最先被删除. 为什么要引入队列? 我们知道代码的执行流有异步与同步之分,例如 var a