當前位置:
首頁 > 知識 > jquery源碼 Callback

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,立即跳出循環

jquery源碼 Callback


三、更多用法

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上。

jquery源碼 Callback

1、add源碼

主要是把回調函數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)