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 ); }); }, /*略*/ })
推荐阅读
-
jQuery 源码分析(十一) 队列模块 Queue详解
-
jQuery 源码分析(十三) 数据操作模块 DOM属性 详解
-
jQuery 源码分析(十二) 数据操作模块 html特性 详解
-
jQuery 源码分析(十九) DOM遍历模块详解
-
jQuery 源码分析(二十一) DOM操作模块 删除元素 详解
-
jQuery 源码分析(十五) 数据操作模块 val详解
-
jQuery 源码解析(八) 异步队列模块 Callbacks 回调函数详解
-
jQuery源码分析(九) 异步队列模块 Deferred 详解
-
jQuery 源码分析(十六) 事件系统模块 底层方法 详解
-
jQuery 源码分析(十七) 事件系统模块 实例方法和便捷方法 详解