jQuery原始碼閱讀(十二)---Callbacks回撥物件
還記得jQuery原始碼閱讀(一)的時候,整理了jQuery庫的整體架構,主要分為三個模組:
入口模組、底層支援模組和功能模組,各個模組之間也是有關聯的。
前面幾篇博文分別分析了jQuery庫的入口模組,最主要的是init方法的分析;還分析了jQuery底層支援模組中的工具方法,主要是通過jQuery.extend()方法來擴充套件jQuery靜態方法的。底層支援模組中,出了工具方法之外,還有很多模組,個人覺得這些模組的一個作用就是增強程式碼的複用性和易用性。比如回撥物件模組,非同步佇列模組,延遲物件,佇列,資料快取,瀏覽器支援模組,Sizzle選擇器模組等等。這次主要先整理jQuery中的回撥物件。
原始碼框架及使用
jQuery.Callbacks = function(flags){
add = function(){
}
fire = function(){
}
self = {
//定義了一些變數
//下面是用來管理函式的方法
add: function(){
},
fire: function(){
},
fireWith: function(){
},
has: function(){
},
fired: function(){
},
lock: function(){
},
locked: function(){
}
//等等
}
return self;
}
由上面架構可以知道,jQuery.Callbacks函式主要是返回一個回撥物件,這個回撥物件通過一些方法來管理回撥函式。具體什麼方法呢?有add(),fire(),fireWith(),lock()等等很多方法,主要是通過將函式加入到陣列列表中的方式,然後一個個去執行從而達到管理函式的目的。
可以看到,Callbacks方法裡面,定義了兩個私有函式,為什麼要這麼做呢?
我的理解是:為了減少記憶體的佔用。因為Callbacks函式最終是要返回一個回撥物件的,而回撥物件中有管理函式的一些方法,如果把這些方法直接寫到回撥物件中,也是可以的,但是這就意味著每個回撥物件都會有一份自己的方法,只是這些方法都是同名的,這就導致記憶體的浪費。而把add和fire定義成兩個私有的變數,不管呼叫多少次jQuery.Callbacks方法,返回的回撥物件中的方法都會去調Callbacks函式中的私有方法,並且指向的是相同的記憶體地址。
下來我們看下jQuery.Callbacks的引數情況:
1. once //只執行一遍函式,不會重複執行
2. memory //會將所有add進函式列表的函式執行
3. unique //不允許列表中有相同的函式
4. stopOnFalse //對於返回值為false的函式,執行之後停止對後面函式的執行
首先來體會下這幾個引數的作用:
function a1(){
console.log('111');
return false;
}
function a2(){
console.log('222');
}
分別展示四組圖來顯示Callbacks中四個引數的意義:
- once標誌
- memory標誌
- unique標誌
- stopOnFalse標誌
從上面四組圖可以很容易理解四個引數的含義,那麼這四個引數具體在原始碼中是如何影響的?我想用下面的圖來說明
原始碼分析
根據上面那個圖,我們就大致應該知道:
add函式中,將函式fn Push到list陣列裡,同時會判斷unique和memory兩個引數,如果unique引數為真,那麼不會將相同的函式都push到陣列裡;如果memory為真,會add之後再調一次fire函式。
fire函式中,將陣列中所有的函式進行執行,同時會判斷once和stopOnFalse兩個引數。如果once為真,只會將所有函式執行一遍;如果stopOnFalse為真,那麼對於返回值為false的函式,執行之後不會再對後面的函式執行。
基於上面兩塊思路來看原始碼,會更好理解一些。
add方法
回撥物件中的add方法:
add: function() {
if ( list ) {
var length = list.length;
//這裡的add方法是Callbacks函式中的私有方法
add( arguments );
if ( firing ) {
firingLength = list.length;
} else if ( memory && memory !== true ) {
//判斷memory引數,並且調fire函式
firingStart = length;
fire( memory[ 0 ], memory[ 1 ] );
}
}
return this;
}
Callbacks函式中的私有add方法:
add = function( args ) {
var i,
length,
elem,
type,
actual;
for ( i = 0, length = args.length; i < length; i++ ) {
elem = args[ i ];
type = jQuery.type( elem );
if ( type === "array" ) {
//如果引數為陣列,再遞迴調add方法
add( elem );
} else if ( type === "function" ) {
//判斷unique標誌,並且看list陣列中是否有該函式
if ( !flags.unique || !self.has( elem ) ) {
list.push( elem );
}
}
}
}
fire方法
self物件中的fire函式:
fire: function() {
//fire方法去調self物件的fireWith方法,該方法不僅將當前作用域傳了進去,還將fire中的引數也傳進去了
self.fireWith( this, arguments );
return this;
}
fireWith: function( context, args ) {
if ( stack ) { //一開始stack為空陣列,轉換成Boolean值也是真;設定了once,會將stack置為undefined,所以重複的fire都不會真正執行
if ( firing ) { //firing這個標誌是解決巢狀呼叫fire的情況;與stack結合起來處理,stack用於儲存上下文和引數
if ( !flags.once ) {
stack.push( [ context, args ] );
}
} else if ( !( flags.once && memory ) ) { //第一次執行或者未設定once引數多次執行
fire( context, args );
}
}
return this;
}
fire = function( context, args ) {
args = args || [];
//如果未設定memory引數,那麼memory變數為true;
memory = !flags.memory || [ context, args ];
fired = true;
firing = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
//對list中每一個函式執行,如果返回false並且stopOnFalse引數為真,那麼跳出迴圈,不再對後面的函式執行。
if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) {
memory = true; // Mark as halted
break;
}
}
firing = false;
if ( list ) {
if ( !flags.once ) {
if ( stack && stack.length ) {
memory = stack.shift();
self.fireWith( memory[ 0 ], memory[ 1 ] );
}
} else if ( memory === true ) {
self.disable(); //這個用於將stack標記設為undefined
} else {
list = [];
}
}
}
對於Callbacks回撥模組,主要就是這兩個函式,當然,還有像self.has
, self.empty
, self.lock
, self.disable
等方法,不過相比來說,這幾個方法都比較好理解。這裡大概提下lock和disable方法,lock方法是禁止後面的fire函式; 而disable方法會禁止後面要執行的所有操作。總體上來說,Callbacks模組,如果能考慮到所有的情況,並可以沿著原始碼走一遍,那麼對於整個模組已經理解得差不多了。
相關文章
- 原始碼閱讀:SDWebImage(十二)——SDWebImageDownloaderOperation原始碼Web
- jQuery原始碼剖析(三) - Callbacks 原理分析jQuery原始碼
- jQuery 原始碼學習 (三) 回撥函式jQuery原始碼函式
- 閱讀原始碼,HashMap回顧原始碼HashMap
- jQuery原始碼閱讀1—jQuery架構jQuery原始碼架構
- 1.3 - Laravel 5.6 - Callbacks 回撥函式機制Laravel函式
- jQuery原始碼閱讀(十)---jQuery靜態方法分析jQuery原始碼
- 【原始碼閱讀】AndPermission原始碼閱讀原始碼
- jQuery原始碼閱讀(十三)---jQuery非同步佇列模組jQuery原始碼非同步佇列
- jQuery CallbacksjQuery
- Qt原始碼閱讀(三) 物件樹管理QT原始碼物件
- 閱讀 jQuery 原始碼的18個驚喜jQuery原始碼
- ThinkPHP6 原始碼閱讀(十二):系統服務PHP原始碼
- jQuery原始碼剖析(四) - Deferred非同步回撥解決方案jQuery原始碼非同步
- jQuery原始碼閱讀(九)---ready函式理解jQuery原始碼函式
- 【原始碼閱讀】Glide原始碼閱讀之with方法(一)原始碼IDE
- 【原始碼閱讀】Glide原始碼閱讀之into方法(三)原始碼IDE
- 閱讀原始碼,通過LinkedList回顧基礎原始碼
- 原始碼閱讀:AFNetworking(十二)——UIButton+AFNetworking原始碼UI
- TiDB 原始碼閱讀系列文章(十二)統計資訊(上)TiDB原始碼
- ReactorKit原始碼閱讀React原始碼
- AQS原始碼閱讀AQS原始碼
- CountDownLatch原始碼閱讀CountDownLatch原始碼
- HashMap 原始碼閱讀HashMap原始碼
- delta原始碼閱讀原始碼
- 原始碼閱讀-HashMap原始碼HashMap
- NGINX原始碼閱讀Nginx原始碼
- Mux 原始碼閱讀UX原始碼
- HashMap原始碼閱讀HashMap原始碼
- fuzz原始碼閱讀原始碼
- RunLoop 原始碼閱讀OOP原始碼
- express 原始碼閱讀Express原始碼
- muduo原始碼閱讀原始碼
- stack原始碼閱讀原始碼
- AmplifyImpostors原始碼閱讀原始碼
- 【原始碼閱讀】Glide原始碼閱讀之load方法(二)原始碼IDE
- PostgreSQL 原始碼解讀(3)- 如何閱讀原始碼SQL原始碼
- jQuery原始碼閱讀(十一)---each、map、grep、merge、makeArray、inArray解讀jQuery原始碼