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

在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", [] );

举个栗子:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Document</title>
  6. <script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
  7. </head>
  8. <body>
  9. <p>123</p>
  10. <script>
  11. function f1(){console.log('f1触发');}    //定义两个测试函数
  12. function f2(){console.log('f2触发')};
  13.  
  14. $('p').queue('test',f1);   //将f1入队,队列名称为test
  15. $('p').dequeue('test');     //将匹配元素的名称为test的函数列表出队并执行
  16. $('p').queue(f2);         //将f2放入p匹配元素的队列中,默认为动画队列,会自动执行。
  17. </script>
  18. </body>
  19. </html>

输出如下:

源码分析


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

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Document</title>
  6. <script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
  7. </head>
  8. <body>
  9. <p>123</p>
  10. <script>
  11. function f1(){console.log('f1触发');}
  12. $('p').queue('test',f1);
  13. </script>
  14. </body>
  15. </html>

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

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

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

  1. jQuery.extend({
  2. /*略*/
  3. queue: function( elem, type, data ) { //返回或修改匹配元素关联的队列。elem是DOM元素或JavaScript对象,type是队列名称,data是可选的函数或函数数组
  4. var q;
  5. if ( elem ) {
  6. type = ( type || "fx" ) + "queue"; //修正参数type,默认为动画队列fx,在参数type后面加上queue表示这是一个队列
  7. q = jQuery._data( elem, type ); //取出参数type对应的队列 如果之前有数据则返回该数组 否则 q等于undefined
  8.  
  9. // Speed up dequeue by getting out quickly if this is just a lookup
  10. if ( data ) { //如果传入了data参数
  11. if ( !q || jQuery.isArray(data) ) { //如果type队列不存在,或者type队列存在且data是一个数组
  12. q = jQuery._data( elem, type, jQuery.makeArray(data) ); //调用jQuery.makeArray把参数data转换为数组并替换队列
  13. } else {
  14. q.push( data ); //队列存在且data不是一个数组则调用数组push方法把参数data放入队列
  15. }
  16. }
  17. return q || [];
  18. }
  19. },
  20.  
  21. dequeue: function( elem, type ) { //用于出队并执行匹配元素关联的函数队列中的下一个元素
  22. type = type || "fx"; //修正参数type,默认是"fx";
  23.  
  24. var queue = jQuery.queue( elem, type ), //获取elem元素的type队列
  25. fn = queue.shift(), //调用shift方法取出队列第一个函数
  26. hooks = {}; //存放出队的函数在执行时的数据
  27.  
  28. // If the fx queue is dequeued, always remove the progress sentinel
  29. if ( fn === "inprogress" ) { //如果出队的是占位符"inprogress",则丢弃再从队列头部出一个,只有动画队列会设置占位符"inprogress"
  30. fn = queue.shift();
  31. }
  32.  
  33. if ( fn ) {
  34. // Add a progress sentinel to prevent the fx queue from being
  35. // automatically dequeued
  36. if ( type === "fx" ) { //如果是动画队列
  37. queue.unshift( "inprogress" ); //则在队列开头添加一个占位符"inprogress",表示动画函数正在执行当中
  38. }
  39.  
  40. jQuery._data( elem, type + ".run", hooks ); //设置内部数据type+".run",表示参数type对应的队列正在执行,值是hooks,它会被作为第二个参数传递给出队的函数
  41. fn.call( elem, function() {
  42. jQuery.dequeue( elem, type );
  43. }, hooks ); //调用函数方法call执行出队的函数,elem是函数执行的上下文,即关键词this指向的对象;第二个参数是封装了jQuery.dequeue( elem, type )的函数,不会自动执行,需要在出队的函数返回前手动调用next()
  44. }
  45.  
  46. if ( !queue.length ) { //如果参数type对应的队列在出队后成为空队列,即所有函数都已经出队并执行
  47. jQuery.removeData( elem, type + "queue " + type + ".run", true ); //调用jQuery.removeData()方法移除参数type对应的数据缓存对象
  48. handleQueueMarkDefer( elem, type, "queue" ); //检查匹配元素关联的队列(type+"queue")和计数器(type+"mark")是否完成,如果完成则触发方法.promise()中的计数器
  49. }
  50. }
  51. });

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

  1. jQuery.fn.extend({
  2. queue: function( type, data ) { //返回第一个匹配元素的函数队列,或修改所有匹配的元素关联的函数队列。type是队列名称,默认是fx。data参数等同于jQuery.queue中的data参数
  3. if ( typeof type !== "string" ) { //修正参数 当传入的格式是queue()或queue(data) (注:data可以是函数或函数数组)时
  4. data = type;
  5. type = "fx";
  6. }
  7.  
  8. if ( data === undefined ) { //如果没有传入data参数,
  9. return jQuery.queue( this[0], type ); //则调用jQuery.queue()返回第一个匹配元素上参数type对应的队列
  10. }
  11. return this.each(function() { //如果传入了data参数
  12. var queue = jQuery.queue( this, type, data ); //为每一个匹配元素调用jQuery.queue( this, type, data ),把参数(函数)入队,或者用参数data(函数数组)替换队列。
  13.  
  14. if ( type === "fx" && queue[0] !== "inprogress" ) { //对于动画队列fx,且没有动画函数正在执行,则立即出队并执行动画函数。
  15. jQuery.dequeue( this, type );
  16. }
  17. });
  18. },
  19. dequeue: function( type ) { //出队并执行匹配元素关联的函数队列中的下一个函数
  20. return this.each(function() {
  21. jQuery.dequeue( this, type );
  22. });
  23. },
  24. /*略*/
  25. })

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

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

    ready事件是当DOM文档树加载完成后执行一个函数(不包含图片,css等),因此它的触发要早于load事件.用法: $(document).ready(fun) ;fun是一个函数,这样当DOM树加 ...

  2. jQuery 源码分析(二) 入口模块

    jQuery返回的对象本质上是一个JavaScript对象,而入口模块则可以保存对应的节点的引用,然后供其它模块操作 我们创建jQuery对象时可以给jQuery传递各种不同的选择器,如下: fals ...

  3. jQuery 源码分析(十七) 事件系统模块 实例方法和便捷方法 详解

    实例方法和便捷方法是指jQuery可以直接通过链接操作的方法,是通过调用$.event上的方法(上一节介绍的底层方法)来实现的,常用的如下: on(types,selector,data,fn,one ...

  4. Netty源码分析之Reactor线程模型详解

    上一篇文章,分析了Netty服务端启动的初始化过程,今天我们来分析一下Netty中的Reactor线程模型 在分析源码之前,我们先分析,哪些地方用到了EventLoop? NioServerSocke ...

  5. vuex 源码分析(七) module和namespaced 详解

    当项目非常大时,如果所有的状态都集中放到一个对象中,store 对象就有可能变得相当臃肿. 为了解决这个问题,Vuex允许我们将 store 分割成模块(module).每个模块拥有自己的 state ...

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

    回顾 上一篇说到:ZRender源码分析4:Painter(View层)-中,这次,来补充一下具体的shape 关于热区的边框 以圆形为例: document.addEventListener('DO ...

  7. Jvm(jdk8)源码分析1-java命令启动流程详解

    JDK8加载源码分析 1.概述 现在大多数互联网公司都是使用java技术体系搭建自己的系统,所以对java开发工程师以及java系统架构师的需求非常的多,虽然普遍的要求都是需要熟悉各种java开发框架 ...

  8. 10.Spark Streaming源码分析:Receiver数据接收全过程详解

    原创文章,转载请注明:转载自 听风居士博客(http://www.cnblogs.com/zhouyf/)   在上一篇中介绍了Receiver的整体架构和设计原理,本篇内容主要介绍Receiver在 ...

  9. mybatis 源码分析(三)Executor 详解

    本文将主要介绍 Executor 的整体结构和各子类的功能,并对比效率: 一.Executor 主体结构 1. 类结构 executor 的类结构如图所示: 其各自的功能: BaseExecutor: ...

随机推荐

  1. 《IM开发新手入门一篇就够:从零开发移动端IM》

        登录 立即注册 TCP/IP详解 资讯 动态 社区 技术精选 首页   即时通讯网›专项技术区›IM开发新手入门一篇就够:从零开发移动端IM   帖子 打赏 分享 发表评论162     想开 ...

  2. Eureka工作原理及它和ZooKeeper的区别

    1.Eureka 简介: Eureka 是 Netflix 出品的用于实现服务注册和发现的工具. Spring Cloud 集成了 Eureka,并提供了开箱即用的支持.其中, Eureka 又可细分 ...

  3. 解决 vscode 中 nuget 插件无法获取包版本的问题

    解决 vscode 中 nuget 插件无法获取包版本的问题 1.问题描述 大概在今年的7月份左右,我忽然发现 NuGet Package Manager 拓展没法正常使用了,只能查询到包: 选完包之 ...

  4. Java 面试宝典!并发编程 71 道题及答案全送上!

    金九银十跳槽季已经开始,作为 Java 开发者你开始刷面试题了吗?别急,我整理了71道并发相关的面试题,看这一文就够了! 1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程( ...

  5. 【朝花夕拾】Android自定义View篇之(七)Android事件分发机制(下)滑动冲突解决方案总结

    前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/11072989.html],谢谢! 前面两篇文章,花了很大篇幅讲解了Android的事件分发机制 ...

  6. Python 从入门到进阶之路(五)

    之前的文章我们简单介绍了一下 Python 的函数,本篇文章我们来看一下 Python 中的面向对象. Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是 ...

  7. MyBatis的结构和配置

    概述 MyBatis将用户从JDBC的访问中解放出来,用户只需要定义需要操作的SQL语句,无须关注底层的JDBC操作,就可以面向对象的方式进行持久层操作.底层数据库连接的获取.数据访问的实现.事务控制 ...

  8. HTML 本地存储

    HTML 本地存储:优于 cookies. 什么是 HTML 本地存储? 通过本地存储(Local Storage),web 应用程序能够在用户浏览器中对数据进行本地的存储. 在 HTML5 之前,应 ...

  9. vue $emit $on 从子组件传递数据给父组件

    原理是: 子组件使用$emit发送数据,父组件使用$on,或者v-on绑定, 来监听子组件发送的数据. 子组件: <button @click="sendChildData" ...

  10. RAID&LVM有关磁盘的故障

    目录 RAID&LVM有关磁盘的故障 RAID 注意:RAID硬盘失效处理--热备和热拔插 RAID实战 LVM介绍 磁盘故障 RAID&LVM有关磁盘的故障 RAID 好处:1.更多 ...