隊列是常用的數據結構之一,只允許在表的前端(隊頭)進行刪除操作(出隊),在表的後端(隊尾)進行插入操作(入隊)。特點是先進先出,最先插入的元素最先被刪除。 在jQuery內部,隊列模塊為動畫模塊提供基礎功能,負責存儲動畫函數、自動出隊並執行動畫函數,同時還要確保動畫函數的順序執行。 jQuery的靜 ...
隊列是常用的數據結構之一,只允許在表的前端(隊頭)進行刪除操作(出隊),在表的後端(隊尾)進行插入操作(入隊)。特點是先進先出,最先插入的元素最先被刪除。
在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 ); }); }, /*略*/ })