前言:queue()
方法和dequeue()
方法是爲 jQuery 的動畫服務的,目的是爲了容許一系列動畫函數被異步調用,但不會阻塞程序。javascript
因此這篇是爲jQuery的動畫解析作準備的。php
1、$
.queue()、$
.dequeue() 和 $()
.queue()、$()
.dequeue() 的區別
(1)$().queue()
和$().dequeue()
這倆是jQuery.fn.extend()
中的方法,也就是供開發者使用的方法,其內部會分別調用 $.queue()
和$.dequeue()
方法。css
//源碼4686行
jQuery.fn.extend( {
queue: function( type, data ) {
xxx
return jQuery.queue( this[ 0 ], type )
},
dequeue: function( type, data ) {
return jQuery.dequeue( this, type )
},
})
複製代碼
(2)$.queue()
和$.dequeue()
這倆是jQuery.extend()
中的方法,也就是 jQuery 內部使用的方法。html
//源碼4594行
jQuery.extend( {
queue: function( elem, type, data ) {},
dequeue: function( elem, type ) {},
})
複製代碼
2、$
().queue()
做用1:
做爲setter
,將function(){}
存進特定隊列中。java
<div id="A" style="background-color: deeppink">這是A</div>
<script>
function a() {
console.log('a','a34')
}
function b() {
console.log('b','b37')
}
//將a、b方法存在類型爲type的隊列裏
//jQuery.fn.queue 給jQuery對象$("A")
/*setter*/
$("#A").queue('type', a)
$("#A").queue('type', b)
</script>
複製代碼
做用2:
做爲getter
,取出特定隊列中function(){}
的數組。web
/*getter*/
$("#A").queue('type') //[a,b]
複製代碼
源碼:數組
jQuery.fn.extend( {
//入隊
//源碼4663行
//'type', function(){xxx}
queue: function( type, data ) {
var setter = 2;
if ( typeof type !== "string" ) {
data = type;
type = "fx";
setter--;
}
//若是參數小於setter,就執行jQuery.queue()方法
/*這邊就是getter*/
if ( arguments.length < setter ) {
//this[0] 目標DOM元素
//type "type"
//返回[function a(){xxx},function b(){xxx}]
return jQuery.queue( this[ 0 ], type );
}
//若是data是undefined就返回jQuery對象
return data === undefined ?
this :
this.each( function() {
/*這邊是setter*/
var queue = jQuery.queue( this, type, data );
// Ensure a hooks for this queue
//確保該隊列有一個hooks
//返回{empty:{
// 裏面是jQuery.Callbacks方法
// 其中add方法被改寫
// }}
jQuery._queueHooks( this, type );
/*專門爲fx動畫作處理*/
if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
jQuery.dequeue( this, type );
}
} );
},
})
複製代碼
解析:
不涉及 fx 動畫的話,本質是調用的內部的jQuery.queue()
方法
(1)若是不足兩個參數的話,就調用jQuery. queue()
來get
獲取數據。
(2)若是大於等於兩個參數的話,就調用jQuery. queue()
來set
存數據,而且調用jQuery._queueHooks()
,用來生成一個queueHooks
對象或者返回當前值。
(3)若是是 fx 動畫,而且隊頭沒有inprogress
鎖的話,就執行jQuery.dequeue()
方法。緩存
3、jQuery._queueHooks()
做用:
若是目標元素的數據緩存(dataPriv
)已存在名稱type
+queueHooks
的Hooks
話,則直接返回該Hooks,
不然返回有empty
屬性的jQuery.Callback()
方法生成的對象:閉包
其中的fire()
方法用來清除隊列。app
源碼:
// Not public - generate a queueHooks object, or return the current one
//jQuery內部方法,生成一個queueHooks對象或者返回當前值
//目標元素,"type"
//源碼4676行
_queueHooks: function( elem, type ) {
//typequeueHooks
var key = type + "queueHooks";
//若是dataPriv已存在名稱typequeueHooks的Hooks話,則直接返回該Hooks
//不然返回有empty屬性的jQuery.Callback()方法生成的對象
return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {
empty: jQuery.Callbacks( "once memory" ).add( function() {
dataPriv.remove( elem, [ type + "queue", key ] );
} )
} );
}
複製代碼
解析:jQuery.Callbacks()
方法會放到$.dequeue
後講解
4、jQuery.queue()
做用:
把callback
依次存入目標元素的queue
中,或者取出queue
。
源碼:
jQuery.extend( {
//做用:目標元素可執行的任務隊列
//源碼4596行
//elem 目標元素
//$("#A"),"type",function(){xxx}
queue: function( elem, type, data ) {
var queue;
if ( elem ) {
//typequeue
type = ( type || "fx" ) + "queue";
//從數據緩存中獲取typequeue隊列,若是沒有則爲undefined
queue = dataPriv.get( elem, type );
// Speed up dequeue by getting out quickly if this is just a lookup
if ( data ) {
//若是queue不存在,或者data是Array的話
//就建立queue,queue=[data1,data2,...]
if ( !queue || Array.isArray( data ) ) {
queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
}
//queue存在的話,就把data push進去
else {
queue.push( data );
}
}
//queue=[a,b]
return queue || [];
}
},
})
複製代碼
解析:
(1)做爲setter
function a() {
console.log('a','a34')
}
$("#A").queue('type', a)
複製代碼
此時data
存在,而且是第一次建立type='type'
的queue
,因此使用dataPriv.access( elem, type, jQuery.makeArray( data ) )
來建立queue
,並把function a
push 進queue
中。
(2)做爲getter
$("#A").queue('type') //[a,b]
複製代碼
此時data
不存在,直接從數據緩存中獲取queue
並返回。
注意:jQuery.queue()
始終返回queue
數組,而$().queue()
會返回 jQuery 對象或者是queue
數組。
5、$().dequeue()
做用:
移出隊頭的函數並執行該callback
。
源碼:
jQuery.fn.extend( {
//出隊
//移出隊頭的函數並執行它
//源碼4717行
dequeue: function( type ) {
return this.each( function() {
jQuery.dequeue( this, type );
} );
},
})
複製代碼
解析:
其實就是執行$.dequeue()
函數。
6、jQuery.dequeue()
做用:
同五。
源碼:
jQuery.extend( {
//源碼4624行
//目標元素,'type'
dequeue: function( elem, type ) {
//'type'
type = type || "fx";
//get,獲取目標元素的隊列
var queue = jQuery.queue( elem, type ),
//長度
startLength = queue.length,
//去除對首元素,並返回該元素
fn = queue.shift(),
//確保該隊列有一個hooks
hooks = jQuery._queueHooks( elem, type ),
//next至關於dequeue的觸發器
next = function() {
jQuery.dequeue( elem, type );
};
// If the fx queue is dequeued, always remove the progress sentinel
//若是fn='inprogress',說明是fx動畫隊列正在出隊,就移除inprogress
if ( fn === "inprogress" ) {
fn = queue.shift();
startLength--;
}
if ( fn ) {
// Add a progress sentinel to prevent the fx queue from being
// automatically dequeued
//若是是fx動畫隊列的話,就添加inprogress標誌,來防止自動出隊執行
//意思應該是等上一個動畫執行完畢後,再執行下一個動畫
if ( type === "fx" ) {
queue.unshift( "inprogress" );
}
// Clear up the last queue stop function
//刪除hooks的stop屬性方法
delete hooks.stop;
//遞歸dequeue方法
fn.call( elem, next, hooks );
}
console.log(startLength,'startLength4669')
//若是隊列是一個空數組,而且hooks存在的話,清除該隊列
if ( !startLength && hooks ) {
console.log('aaaa','bbbb4671')
//進行隊列清理
hooks.empty.fire();
}
},
})
複製代碼
解析:
(1)inprogress
應該是一個鎖,當 fx 動畫執行動畫 A 的時候,就加鎖,當動畫 A 執行完畢後,就解鎖,再去運行下一個動畫。
(2)注意下fn.call( elem, next, hooks )
,保持fn
的this
是element
的同時,給fn
傳的兩個參數,分別爲next
和hooks
,方便操做。
(3)當queue
是空數組的時候,就觸發hooks.empty
的fire()
方法,將queue
清除。
7、jQuery.Callbacks()
做用:jQuery
的callbacks
回調方法,返回一個object,裏面包含 a、b、c 方法,在執行任意一個方法後,這個方法依舊返回 a、b、c 方法,因此jQuery.Callbacks()
是鏈式調用的關鍵函數。
在_queueHooks
中有用到該函數:
dataPriv.access( elem, key, {
empty: jQuery.Callbacks( "once memory" ).add( function() {
dataPriv.remove( elem, [ type + "queue", key ] );
} )
} );
複製代碼
源碼:
/*建立一個使用如下參數的callback列表
* Create a callback list using the following parameters:
* options:是一個可選的空格分開的參數,它能夠改變callback列表的行爲或造成新的option對象
* options: an optional list of space-separated options that will change how
* the callback list behaves or a more traditional option object
* 默認狀況下一個回調列表會表現成一個event callback列表而且會觸發屢次
* By default a callback list will act like an event callback list and can be
* "fired" multiple times.
* option可選值:
* Possible options:
* 確保callback列表只會被觸發一次,好比Deferred對象
* once: will ensure the callback list can only be fired once (like a Deferred)
* 保持跟蹤以前的values,而且會在list用最新的values觸發後,調用該回調函數
* memory: will keep track of previous values and will call any callback added
* after the list has been fired right away with the latest "memorized"
* values (like a Deferred)
* //確保callback只會被添加一次
* unique: will ensure a callback can only be added once (no duplicate in the list)
* //當callbak返回false時打斷調用
* stopOnFalse: interrupt callings when a callback returns false
*
*/
//源碼3407行
//callbacks回調對象,函數的統一管理
//once memory
jQuery.Callbacks = function( options ) {
// Convert options from String-formatted to Object-formatted if needed
// (we check in cache first)
options = typeof options === "string" ?
//options: {once:true,memory:true}
createOptions( options ) :
jQuery.extend( {}, options );
//用來知道list是否正被調用
var // Flag to know if list is currently firing
firing,
// Last fire value for non-forgettable lists
memory,
// Flag to know if list was already fired
fired,
// Flag to prevent firing
locked,
// Actual callback list
list = [],
// Queue of execution data for repeatable lists
queue = [],
// Index of currently firing callback (modified by add/remove as needed)
firingIndex = -1,
//觸發list中的回調函數
// Fire callbacks
fire = function() {
//true
// Enforce single-firing
//'once memory'中的'once'只容許觸發一次
locked = locked || options.once;
// Execute callbacks for all pending executions,
// respecting firingIndex overrides and runtime changes
fired = firing = true;
for ( ; queue.length; firingIndex = -1 ) {
//從queue移除第一個元素,並返回該元素
memory = queue.shift();
while ( ++firingIndex < list.length ) {
// Run callback and check for early termination
//memory=[document, Array(1)]
//memory[0]是document
//意思就是讓document去執行add()方法中添加的callback函數
if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
options.stopOnFalse ) {
// Jump to end and forget the data so .add doesn't re-fire
firingIndex = list.length;
memory = false;
}
}
}
// Forget the data if we're done with it
if ( !options.memory ) {
memory = false;
}
firing = false;
// Clean up if we're done firing for good
//若是once:true,清空list數組
if ( locked ) {
// Keep an empty list if we have data for future add calls
if ( memory ) {
list = [];
// Otherwise, this object is spent
} else {
list = "";
}
}
},
// Actual Callbacks object
self = {
//添加一個回調函數或者是一個回調函數的集合
// Add a callback or a collection of callbacks to the list
add: function() {
if ( list ) {
// If we have memory from a past run, we should fire after adding
if ( memory && !firing ) {
firingIndex = list.length - 1;
queue.push( memory );
}
//閉包
//將arguments做爲參數即args傳入閉包的add方法中
( function add( args ) {
//args[0]即function(){dataPriv.remove( elem, [ type + "queue", key ] ) }
jQuery.each( args, function( _, arg ) {
if ( isFunction( arg ) ) {
//若是self對象沒有該方法,將其push進list中
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
} else if ( arg && arg.length && toType( arg ) !== "string" ) {
// Inspect recursively
add( arg );
}
} );
} )( arguments );
//undefined undefined
if ( memory && !firing ) {
fire();
}
}
//this即self對象
//也就說在調用self對象內的方法後會返回self對象自己
return this;
},
// Remove a callback from the list
remove: function() {
jQuery.each( arguments, function( _, arg ) {
var index;
while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
list.splice( index, 1 );
// Handle firing indexes
if ( index <= firingIndex ) {
firingIndex--;
}
}
} );
return this;
},
// Check if a given callback is in the list.
// If no argument is given, return whether or not list has callbacks attached.
has: function( fn ) {
return fn ?
jQuery.inArray( fn, list ) > -1 :
list.length > 0;
},
// Remove all callbacks from the list
empty: function() {
if ( list ) {
list = [];
}
return this;
},
// Disable .fire and .add
// Abort any current/pending executions
// Clear all callbacks and values
disable: function() {
locked = queue = [];
list = memory = "";
return this;
},
disabled: function() {
return !list;
},
// Disable .fire
// Also disable .add unless we have memory (since it would have no effect)
// Abort any pending executions
lock: function() {
locked = queue = [];
if ( !memory && !firing ) {
list = memory = "";
}
return this;
},
locked: function() {
return !!locked;
},
// Call all callbacks with the given context and arguments
fireWith: function( context, args ) {
if ( !locked ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
queue.push( args );
if ( !firing ) {
fire();
}
}
return this;
},
// Call all the callbacks with the given arguments
fire: function() {
self.fireWith( this, arguments );
return this;
},
// To know if the callbacks have already been called at least once
fired: function() {
return !!fired;
}
};
console.log(queue,'queue3614')
return self;
};
複製代碼
解析:
主要看add()
和fire()
方法
(1)self.add()
注意裏面的閉包函數,使用閉包的目的是凍結args的值,這樣能夠避免異步調用形成的值得改變。
add()方法就是將function() {dataPriv.remove( elem, [ type + "queue", key ] );}
push 進 list 數組中,以供fire()
來調用 list 中的callback。
注意最後返回的是this
,即self
對象,也就說在調用self
對象內的方法後會返回self
對象自己,而self
內部又含有add()、fire()
等方法,經過jQuery.Callbacks
傳入的參數options
來控制可否調用,及調用的次數。
(2)self.fire()
做用是觸發 list 中的回調函數,onece memory
的once
表示只讓fire()
觸發一次後,就須要清理 list,memory
表示是將 list 清空成空數組仍是空字符。
8、createOptions()
做用:
將特定格式的string(空格分開),轉化爲特定格式的object({xxx:true,xxx:true,...}
.
源碼:
//將特定格式的string(空格分開),轉化爲特定格式的object({xxx:true,xxx:true,...})
// Convert String-formatted options into Object-formatted ones
//源碼3377行
//'once memory' —> {once:true,memory:true}
function createOptions( options ) {
var object = {};
jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
object[ flag ] = true;
} );
return object;
}
複製代碼
解析:
將以空格鏈接的字符串,以空格拆開,並做爲 object 的key,其 value 爲 true
好比:
"once memory" => {once:true,memory:true,}
(完)