jquery源碼 Callback
工具方法。對函數的統一管理。
jquery2.0.3版本$.Callback部分的源碼如下:
// String to Object options format cache
var optionsCache = {};
// Convert String-formatted options into Object-formatted ones and store in cache
function createOptions( options ) {
var object = optionsCache[ options ] = {};
jQuery.each( options.match( core_rnotwhite ) || , function( _, flag ) {
object[ flag ] = true;
});
return object;
}
/*
* Create a callback list using the following parameters:
*
* options: an optional list of space-separated options that will change how
* the callback list behaves or a more traditional option object
*
* By default a callback list will act like an event callback list and can be
* "fired" multiple times.
*
* Possible options:
*
* once: will ensure the callback list can only be fired once (like a Deferred)
*
* 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)
*
* unique: will ensure a callback can only be added once (no duplicate in the list)
*
* stopOnFalse: interrupt callings when a callback returns false
*
*/
jQuery.Callbacks = function( options ) {
// Convert options from String-formatted to Object-formatted if needed
// (we check in cache first)
options = typeof options === "string" ?
( optionsCache[ options ] || createOptions( options ) ) :
jQuery.extend( {}, options );
var // Last fire value (for non-forgettable lists)
memory,
// Flag to know if list was already fired
fired,
// Flag to know if list is currently firing
firing,
// First callback to fire (used internally by add and fireWith)
firingStart,
// End of the loop when firing
firingLength,
// Index of currently firing callback (modified by remove if needed)
firingIndex,
// Actual callback list
list = ,
// Stack of fire calls for repeatable lists
stack = !options.once && ,
// Fire callbacks
fire = function( data ) {
memory = options.memory && data;
fired = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
memory = false; // To prevent further calls using add
break;
}
}
firing = false;
if ( list ) {
if ( stack ) {
if ( stack.length ) {
fire( stack.shift );
}
} else if ( memory ) {
list = ;
} else {
self.disable;
}
}
},
// Actual Callbacks object
self = {
// Add a callback or a collection of callbacks to the list
add: function {
if ( list ) {
// First, we save the current length
var start = list.length;
(function add( args ) {
jQuery.each( args, function( _, arg ) {
var type = jQuery.type( arg );
if ( type === "function" ) {
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
} else if ( arg && arg.length && type !== "string" ) {
// Inspect recursively
add( arg );
}
});
})( arguments );
// Do we need to add the callbacks to the
// current firing batch?
if ( firing ) {
firingLength = list.length;
// With memory, if we"re not firing then
// we should call right away
} else if ( memory ) {
firingStart = start;
fire( memory );
}
}
return this;
},
// Remove a callback from the list
remove: function {
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
var index;
while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
list.splice( index, 1 );
// Handle firing indexes
if ( firing ) {
if ( index <= firingLength ) {
firingLength--;
}
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 && list.length );
},
// Remove all callbacks from the list
empty: function {
list = ;
firingLength = 0;
return this;
},
// Have the list do nothing anymore
disable: function {
list = stack = memory = undefined;
return this;
},
// Is it disabled?
disabled: function {
return !list;
},
// Lock the list in its current state
lock: function {
stack = undefined;
if ( !memory ) {
self.disable;
}
return this;
},
// Is it locked?
locked: function {
return !stack;
},
// Call all callbacks with the given context and arguments
fireWith: function( context, args ) {
if ( list && ( !fired || stack ) ) {
args = args || ;
args = [ context, args.slice ? args.slice() : args ];
if ( firing ) {
stack.push( args );
} else {
fire( args );
}
}
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;
}
};
return self;
};
View Code
一、$.Callback的簡單使用及應用場景
1、$.Callback的用法。
觀察者模式,添加完後統一觸發。
function aaa{
alert(1);
}
function bbb{
alert(2);
}
var cb= $.Callbacks;
cb.add(aaa);
cb.add(bbb);
cb.fire;
2、好處,應用場景。
要統一的管理aaa和bbb。有時候如下,很難對不同作用域下的函數進行統一管理。
function aaa{
alert(1);
}
(function{
function bbb{
alert(2);
}
});
aaa;
bbb;
只能彈出1,因為bbb是局部作用域中的。
$callback可以做到。如下,只要cb是全局的。
var cb= $.Callbacks;
function aaa{
alert(1);
}
cb.add(aaa);
(function{
function bbb{
alert(2);
}
cb.add(bbb);
});
cb.fire;
對應複雜情況很有用。統一管理,通過fire統一觸發。
二、原理圖
Callback接收一個參數,可以有4個選項,once,memory,unique,stopOnFalse。
self單體有這些方法:add,remove,has,empty,disable,disabled,lock,locked, fireWith,fire,fired。
list=數組變數,用來收集回調函數。fire的時候對其循環調用。
add:push數組
fire:調用fireWith,fireWith允許傳參,fire可傳可不傳。
fireWith:調用私有函數fire,在私有函數fire中for循環list。
remove:splice數組。
4個參數:
- once針對fire只循環一次
- memory 針對add,作用到add上,add時判斷有memory就去執行fire。
- unique 針對add,添加的時候就可以去重
- stopOnFalse 針對fire,在for循環時遇到false,立即跳出循環
三、更多用法
1、callback4個參數的作用
不傳參數,fire幾次就觸發幾次。
function aaa {
alert(1);
}
function bbb {
alert(2);
}
var cb = $.Callbacks;
cb.add(aaa);
cb.add(bbb);
cb.fire; //1 2
cb.fire;//1 2
View Code
- once:fire只能觸發一次
function aaa {
alert(1);
}
function bbb {
alert(2);
}
var cb = $.Callbacks("once");
cb.add(aaa);
cb.add(bbb);
cb.fire; //1 2
cb.fire;
不傳參數,在fire之後add的回調不能被fire。
//不寫參數,只彈出1,2不會彈出
function aaa {
alert(1);
}
function bbb {
alert(2);
}
var cb = $.Callbacks;
cb.add(aaa);
cb.fire; //1
cb.add(bbb);
View Code
- memory記憶,在fire前面後面add的方法都能得到執行。
function aaa {
alert(1);
}
function bbb {
alert(2);
}
var cb = $.Callbacks("memory");
cb.add(aaa);
cb.fire; //1 2
cb.add(bbb);
- unique:去重
//不加參數,add2次aaa,就會觸發2次aaa
function aaa {
alert(1);
}
var cb = $.Callbacks;
cb.add(aaa);
cb.add(aaa);
cb.fire; //1 1
View Code
function aaa {
alert(1);
}
var cb = $.Callbacks("unique");
cb.add(aaa);
cb.add(aaa);
cb.fire; //1 加了unique參數,同樣的函數不能多次add
- stopOnFalse:函數返回false跳出循環
function aaa {
alert(1);
return false;
}
function bbb {
alert(2);
}
var cb = $.Callbacks;
cb.add(aaa);
cb.add(bbb);
cb.fire; //1 2 不傳參,第一個函數返回false時後面的函數也能正常執行
function aaa {
alert(1);
return false;
}
function bbb {
alert(2);
}
var cb = $.Callbacks("stopOnFalse");
cb.add(aaa);
cb.add(bbb);
cb.fire; //1
//傳參stopOnFalse,第一個函數返回false時後面的函數不再執行
2、callback也可以接收組合的形式
function aaa {
alert(1);
}
function bbb {
alert(2);
}
//組合使用,只執行一次,並且彈出1 2
var cb = $.Callbacks("once memory");
cb.add(aaa);
cb.fire; //1
cb.add(bbb);
cb.fire;
源碼中:傳入了 once和memory後,
options={once:true,memory:true}
optionCache={
"once memory":{once:true,memory:true}
}
6、fire可以傳參
參數作為回調的實參
function aaa(n) {
alert("aaa "+n);
}
function bbb(n) {
alert("bbb "+n);
}
var cb = $.Callbacks;
cb.add(aaa);
cb.add(bbb);
//fire傳參
cb.fire("hello"); //彈出aaa hello 和bbb hello
四、源碼
Callbacks就是一個工具函數,內部定義了一個self ,add和remove還有has等掛在self上。
主要是把回調函數Push到數組list中。
add: function {
if ( list ) { //list初始化為,if判斷會返回true
// First, we save the current length
var start = list.length;
(function add( args ) {
jQuery.each( args, function( _, arg ) { ////處理cb.add(aaa,bbb)這種調用
var type = jQuery.type( arg );//arg就是每一個函數
if ( type === "function" ) {//arg是函數就push到list中,此時有個判斷有沒有unique
if ( !options.unique || !self.has( arg ) ) {//有unique走後面,判斷list中有沒有這個函數,有就不添加了
list.push( arg );
} } else if ( arg && arg.length && type !== "string" ) { //處理cb.add([aaa,bbb])這種調用 // Inspect recursively add( arg );//遞歸分解,最終還是push到list } }); })( arguments ); // Do we need to add the callbacks to the // current firing batch? if ( firing ) { firingLength = list.length; // With memory, if we"re not firing then // we should call right away } else if ( memory ) { firingStart = start; fire( memory ); } } return this; },2、remove源碼
// Remove a callback from the list
remove: function {
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
var index;
while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
list.splice( index, 1 );//主要就是splice刪除操作
// Handle firing indexes
if ( firing ) {
if ( index <= firingLength ) {
firingLength--;
}
if ( index <= firingIndex ) {
firingIndex--;
}
}
}
});
}
return this;
},
3、fire源碼1、整體調用邏輯
self的fire調用self的fireWith,fireWith把參數傳遞到fire函數。
// Call all callbacks with the given context and arguments
fireWith: function( context, args ) {
if ( list && ( !fired || stack ) ) {
args = args || ;
args = [ context, args.slice ? args.slice() : args ];
if ( firing ) {
stack.push( args );
} else {
fire( args );
} } return this; }, // Call all the callbacks with the given arguments fire: function { self.fireWith( this, arguments ); return this; },fire時主要是for循環
// Fire callbacks
fire = function( data ) {
memory = options.memory && data;
fired = true;//fired變為true說明已經調用過一次了,
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
firing = true;//觸發進行時
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {//每次函數調用同時處理stopOnFalse的情況
memory = false; // To prevent further calls using add //stopOnFalse後有memory也不好使了
break;
}
}
firing = false;//觸髮結束
if ( list ) {
if ( stack ) {
if ( stack.length ) {
fire( stack.shift );
}
} else if ( memory ) {
list = ;
} else {
self.disable;
}
}
},
2、firing特殊情況
線通過例子來看一下效果
function aaa {
alert(1);
cb.fire; //在這裡調用fire會出現什麼問題 死循環
}
function bbb {
alert(2);
}
var cb = $.Callbacks;
cb.add(aaa);
cb.add(bbb);
cb.fire;
在執行函數的過程中再次調用fire的執行順序是怎樣的?
var bBtn=true;//用bBtn避免死循環
function aaa {
alert(1);
if(bBtn){
cb.fire;//注意這裡fire調用後執行順序是1 2 1 2,而不是1 1 2 2
bBtn=false;
}
}
function bbb {
alert(2);
}
var cb = $.Callbacks;
cb.add(aaa);
cb.add(bbb);
cb.fire;
結論:把函數運行過程中觸發的fire放到了運行過程的隊列當中。
怎麼做到的,看源碼:
在fireWith的時候判斷for循環有沒有執行完
fireWith: function( context, args ) {
if ( list && ( !fired || stack ) ) {
args = args || ;
args = [ context, args.slice ? args.slice() : args ];
if ( firing ) {//firing在for循環沒有走完時一直是true
stack.push( args )
;//所以這句話意思就是函數執行時再去fire調用就會push到stack數組中 } else { fire( args ); } } return this; },再去調用fire的時候
// Fire callbacks
fire = function( data ) {
memory = options.memory && data;
fired = true;//fired變為true說明已經調用過一次了,
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
firing = true;//觸發進行時
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {//每次函數調用同時處理stopOnFalse的情況
memory = false; // To prevent further calls using add //stopOnFalse後有memory也不好使了
break;
}
}
firing = false;//觸髮結束
if ( list ) {
if ( stack ) { //這就是出現在函數執行過程中再次fire的時候,等循環執行完,再去按順序執行
if ( stack.length ) {
fire( stack.shift )
; } } else if ( memory ) {//只執行一次的時候,有once,memory就清空list,此時fire就相當於一個執行一個空數組 list = ; } else { self.disable;//disable阻止後續任何的fire操作 } } },針對下面這段源碼的一個例子:once和memory同時存在的時候,fire無效因為list為了,但是add仍然有效。
else if ( memory ) {//只執行一次的時候,有once,memory就清空list,此時fire就相當於一個執行一個空數組
list = ;
} else {
self.disable;//disable阻止後續任何的fire操作
}
disable阻止後續任何的fire操作。
function aaa {
alert(1);
}
function bbb {
alert(2);
}
//組合使用,只執行一次,並且彈出1 2 3
var cb = $.Callbacks("once memory");
cb.add(aaa);
cb.fire; //1
cb.fire;//此時list為
cb.add(bbb);
cb.fire;
function ccc{
alert(3);
}
cb.add(ccc);
4、其他源碼
has(fn):判斷list有沒有fn
empty: 清空數組list=
disable:全部鎖住,禁止了,如下
// Have the list do nothing anymore
disable: function {
list = stack = memory = undefined;
return this;
},
disabled:判斷是不是禁止了。return !list;
lock:只是把stack鎖住
// Lock the list in its current state
lock: function {
stack = undefined;
if ( !memory ) {
self.disable;
}
return this;
},
locked:是否locked。 return !stack;
5、 lock和disable的區別disable禁止所有操作
function aaa {
alert(1);
}
function bbb {
alert(2);
}
var cb = $.Callbacks("memory");
cb.add(aaa);
cb.fire; //1
cb.disable;//disable後只能彈出1 因為禁止所有操作了,雖然有Memory
cb.add(bbb);//不起作用了,此時list變為undefined了
cb.fire;//不起作用了
lock只是鎖住數組
function aaa {
alert(1);
}
function bbb {
alert(2);
}
var cb = $.Callbacks("memory");
cb.add(aaa);
cb.fire; //1 2
cb.lock;//lock只是把後續的fire鎖住,其他操作是鎖不住的
cb.add(bbb);
cb.fire;//不起作用了 此時list為
本文作者starof,因知識本身在變化,作者也在不斷學習成長,文章內容也不定時更新,為避免誤導讀者,方便追根溯源,請諸位轉載註明出處:http://www.cnblogs.com/starof/p/6885500.html有問題歡迎與我討論,共同進步。
※Docker下搭建Jenkins構建環境
※javascript——類和模塊
※php jquery+ajax寫批量刪除
※解決tomcat中反序列化找不到class
※Neo4j 第一篇:在Windows環境中安裝Neo4j
TAG:達人科技 |
※在 Grails 中使用 jQuery 和 DataTables
※jQuery Mobile 表格
※jQuery Mobile 事件
※jQuery UI編程的 ThemeRoller
※jQuery Mobile 按鈕
※jQuery Mobile 主題
※jQuery UI 為什麼使用部件庫(Widget Factory)
※jQuery Mobile 過渡
※jQuery Mobile 彈窗
※jQuery Mobile 圖標
※jQuery Mobile 過濾
※jQuery UI 實例-選擇(Selectable)
※jQuery源碼分析之jQuery.event.fix方法五問
※jQuery Mobile 表單
※jQuery Mobile 頁面
※jQuery Mobile編程
※jQuery Mobile 安裝
※jQuery Mobile CSS 類
※jQuery Mobile 實例
※jQuery UI 實例-縮放(Resizable)