首頁  >  文章  >  web前端  >  jQuery 資料快取模組進化史詳細介紹_jquery

jQuery 資料快取模組進化史詳細介紹_jquery

WBOY
WBOY原創
2016-05-16 17:48:10926瀏覽

資料快取系統最早應該是jQuery1.2引進的,那時它的事件系統完成照搬DE大神的addEvent.js,而addEvent在實作有個缺憾,它把事件的回呼都放到EventTarget之上,這會引發循環引用,如果EventTarget是window對象,又會引發全域污染。有了資料快取系統,除了規避這兩個風險外,我們還可以有效地保存不同方法產生的中間變量,而這些變量會對另一個模組的方法有用,解耦方法間的依賴。對jQuery來說,它的事件克隆乃至後來的列隊實作都是離不開快取系統。

jQuery1.2 在core模組新增了兩個靜態方法,data 與removeData。 data不用說,與jQuery其他方法一樣,讀寫結合。 jQuery的快取系統是把所有資料都放$.cache之上,然後為每個要使用快取系統的元素節點,文件物件與window物件分配一個UUID。 UUID的屬性名稱為隨機的自訂屬性,"jQuery" (new Date()).getTime(), 值為整數,從零遞增。但UUID總是要附於一個物件上,如果那個物件是window,豈不是全域污染嗎,因此jQuery內部判定它是window物件時,映射為一個叫windowData的空對象,然後UUID加在它之上。有了UUID,我們在首次存取快取系統時,會在$.cache物件開闢一個空物件(快取體),用於放置與目標物件相關的東西。這有點像銀行開戶了,UUID的價值就是存摺。 removeData則會刪除不再需要保存數據,如果到最後,數據刪清光了,它也沒有任何鍵值對,成為空對象,jQuery就會從$.cache中刪掉此對象,並從目標對象移除UUID。

複製程式碼 程式碼如下:

//jQuery1.2.3 jQuery.extend({
cache: {},
data: function( elem, name, data ) {
elem = elem == window ? windowData : elem;//對window物件做特別處理
var id = elem[ expando ];
if ( !id ) //如果沒有UUID則新設一個
id = elem[ expando ] = uuid;
//如果沒有在$.cache中開戶,則先開戶
if ( name && !jQuery.cache[ id ] )
jQuery.cache[ id ] = {};

// 第三個參數不為undefined時,為寫入操作
if ( data != undefined )
jQuery.cache[ id ][ name ] = data;
//如果只有一個參數,則返回快取對象,兩個參數則返回目標資料
return name ? jQuery.cache[ id ][ name ] : id;
},

removeData: function( elem, name ) {
elem = elem == window ? windowData : elem;
var id = elem[ expando ];
if (name ) { //移除目標資料
if ( jQuery.cache[ id ] ) {
delete jQuery.cache[ id ][ name ];
name = "";

for ( name in jQuery.cache[ id ] )
break;
//遍歷緩存體,如果不為空,那name會改寫,如果沒有被改寫,則!name 為true,
//從而引發再次呼叫此方法,但這次是只傳一個參數,移除緩存體,
if ( !name )
jQuery.removeData( elem );
}
} else {
//移除UUID,但IE下對元素使用delete會拋錯
try {
delete elem[ expando ];
} catch(e){
if ( elem.removeAttribute )
elem.removeAttribute( expando );
}//註銷帳戶
delete jQuery.cache[ id ];
}
}
})

}
}
})


}
})
} }) } })
jQuery在1.2.3中加入了兩個同名的原型方法data與removeData,目的是方便鍊式操作與集化操作。並在data中加入getData, setData的自訂事件的觸發邏輯。 1.3中,資料快取系統終於獨立成一個模組data.js(內部開發時的劃分),並加入了兩組方法,命名空間上的queue與dequeue,原型上的queue與dequeue。 queue的目的很明顯,就是快取一組數據,為動畫模組服務。 dequeue是從一組資料中刪除一個。 複製程式碼 程式碼如下:

//jQuery1.3
  jQuery.extend({
   queue: function( elem, type, data ) {
   "fx") "queue";
   var q = jQuery.data( elem, type );
   if ( !q || jQuery.isArray(data) )//確保儲存的是一個陣列

q = jQuery.data( elem, type, jQuery.makeArray(data) );
   else if( data )//然後往這個資料加東西
   q.push( data ); >   return q;
   },
   dequeue: function( elem, type ){
   var queue = jQuery.queue( elem, fn刪掉一個,早期它是放置動畫的回調,刪掉它就call一下,
   // 但沒有做是否為函數的判定,估計也沒有寫到文檔中,為內部使用
   if( ! type || type === "fx" )
   fn = queue[0];
   if( fn !== undefined )
   fn.call(elem); )



fx模塊animate方法的調用示例:




複製代碼
程式碼如下: //each是並行處理多個動畫,queue是一個接一個處理多個動畫  this[ optall.queue === false ? "each" : "queue " ](function(){ /*略*/})
在元素上加入自訂屬性,也會引發一個問題。如果我們對這個元素進行拷貝,就會將此屬性也會複製過去,導致兩個元素都有相同的UUID值,出現資料被錯誤操作的情況。 jQuery早期的複製節點實作非常簡單,如果元素的cloneNode方法不會複製事件就使用cloneNode,否則使用元素的outerHTML,或父節點的innerHTML,用clean方法解析一個新元素出來。但outerHTML與innerHTML都會明確屬性寫在裡面,因此需要用正規則清除它們。



複製程式碼
程式碼如下:  〜//jQuery1.3clone 方法. >  var ret = this.map(function(){    if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {    var html = this var html; ) {
   var div = this.ownerDocument.createElement("div");
   div.appendChild( this.cloneNode(true) );
   html = div.HT;
   return jQuery.clean([html.replace(/ jQueryd ="(?:d |null)"/g, "").replace(/^s*/, "")])[0];
   } else
   return this.cloneNode(true);
  });


jQuery1.4發現IE如果對於有什麼古老的 applet入外部資源的標籤可能會拋錯。由於舊式IE的元素節點只是COM的包裝,一旦引入資源後,它就會變成那種資源的實例,而它們會有嚴格的存取控制,不能像普通的JS物件那樣隨意添加成員。於是jQuery便一刀換,但凡是這三種標籤,就不為它快取資料。 jQuery弄了一個叫noData的hash,用來偵測元素節點的標籤。




複製程式碼

程式碼如下:

  noData: { 『 "object": true,    "applet": true  },   //代碼防禦  if ( elem.nodeName && jQuery.noData[elem>  if ( elem.nodeName && jQuery.noData[elem no)[elem》.L. ;   }
jQuery1.4也對$.data進行改進,允許第二個參數為對象,方便儲存多個資料。 UUID對應的自訂屬性expando 也放進命名空間之下了。 queue與dequeue方法被剝離成一個新模組。
jQuery1.43帶來三項改進。
首先是加入changeData自訂方法。不過這套方法沒有什麼銷售量,只是產品經理的自戀吧。
偵測元素節點是否支援新增自訂屬性的邏輯被獨立成一個叫做acceptData的方法。因為jQuery團隊發現當object標籤載入的flash資源,它還是可以加入自訂屬性的,於是決定對此情況網開一面。 IE在載入flash時,需要對object指定一個叫classId的屬性,值為clsid:D27CDB6E-AE6D-11cf-96B8-444553540000,因此檢測邏輯就變得非常複雜,由於data, removeData都要用到,獨立出來有效節省比特。
HTML5對人們隨便加入自訂屬性的行為做出回應,新增一種叫做"data-*"的快取機制。當使用者設定的屬性以"data-"開頭,它們會被儲存到元素節點的dataset物件上。這就導致人們可能用HTML5方便緩存數據,也可能用jQuery的快取系統保存數據,那麼data方法就變得有點不中用了。於是jQuery在原型上的data做了增強,當使用者第一次造訪此元素節點,會遍歷它所有"data-"開頭的自訂屬性(為了照顧舊式IE,不能直接遍歷dataset),把它們放到jQuery的快取體中。那麼當使用者取資料時,會先從快取系統中,沒有再使用setAttribute存取"data-"自訂屬性。但HTML5的快取系統非常弱,只能保存字串(這當然是出於循環引用的考量),於是jQuery會將它們還原為各種資料類型,如"null",, "false", "true"變成null, false, true, 符合數字格式的字串會轉換成數字,如果它是以"{"開頭"}"結尾則嘗試轉成一個物件。
複製程式碼 程式碼如下:

 〜//jQuery1.43.fn. >  rbrace = /^(?:{.*}|[.*])$/;
  if ( data === undefined && this.length ) {
   data = jQuery.data( this[0] 、 );
   if ( data === undefined && this[0].nodeType === 1 ) {
   data = this[0].getAttribute( "data-" key ); 🎜>   if ( typeof data === "string" ) {
   try {
   data = data === "true" ? true :
   data = data === "true" ? true :
    data = 7:" data === "null" ? null :
   !jQuery.isNaN( data ) ? parseFloat( data ) :
   rbrace.test( data ) ? jery.parseJSON( data ) : >   } catch( e ) {}
  
   } else {
   data = undefined;
   } jQuery1.5也帶來三項改進。當時jQuery已經在1.42打敗Prototype.js,如日中天,馬太效應,使用者量暴增。它的重點改為提升效能,進入fix bug階段(用戶多,相當於免費的測試員就越多,測試覆蓋率就越大)。
改進expando,原來是基於時間截,現在是版本號碼加隨機數。因此使用者可能在一個頁面引入多個版本的jQuery。
是否有此資料的邏輯被抽出成一個hasData方法,處理HTML5的"data-*"屬性也被抽出成一個私有方法dataAttr。它們都是為了邏輯看起來更清晰。 dataAttr使用JSON.parse,由於這個JSON可能是JSON2.js引入的,而JSON2.js有個非常糟糕的地方,就是為一系列原生類型添加了toJSON方法,導致for in 迴圈判定是否為空物件出錯。 jQuery被逼搞了個isEmptyDataObject方法做處理。
jQuery的資料快取系統本來就是為事件系統服務而分化出來的,到後來,它是內部眾多模組的基礎設施。換言之,它內部會儲存許多框架使用者的變數(系統資料),但一旦它公開到文件中,使用者也會使用data來保存他們務業中使用的資料(使用者資料)。以前,使用者小,變數名衝突的可能性比較少,加之jQuery為這些系統資料精挑了一些不常用的名字,__class__, __change__或加個後綴什麼的,沒有收到什麼投訴。當jQuery成為世界級的著名框架後,使用者資料名稱幹掉系統資料名,導致事件系統或其他什麼模組癱瘓就時有發生。 jQuery開始將快取體改造,原來就是一個對象,什麼資料都往裡面拋。現在它就這個快取體內開闢一個子對象,鍵名為隨機的jQuery.expando值,如果是系統資料就存到裡面去。但events系統資料為了向前相容起見,還是直接放到快取體之上。至於,如何區分是系統數據,非常簡單,直接在data方法加入第四個參數,真值時為系統數據。 removeData時也相應提供第三個參數,用於刪除系統資料。還新設了一個_data方法,專門用於作業系統資料。以下是緩存體的結構圖:
 
複製程式碼 程式碼如下:
>
jQuery14312343254:{/*放置系統資料*/}
events: {/"放置事件名稱與它對應的回調清單"/}
/*這裡放置使用者資料*/
}

jQuery1.7對緩存體做了改進,系統變數變放置data物件中,為此判定緩存體為空也要做相應的改進,現在要跳過toJSON與data。新架構如下:

複製程式碼 程式碼如下:
var cache = { :{/*放置使用者資料*/}
/*這裡放置系統資料*/
}


jQuery1.8曾經新增一個叫deleteIds的陣列,用於重複UUID,但曇花一現。 UUID的值從1.8起不用jQuery.uuid的了,改用jQuery.guid遞增生成。重大的改進在jQuery1.83後,操作資料的實作被抽出為私有方法,命名空間與原型上的方法只是一個代理,並分成兩組方法,操作使用者資料的data, removeData,作業系統資料的_data ,_removeData。現在光是快取系統就是一個龐大家族了。

 
說到底,資料快取就是在目標物件與快取體間建立一對一的關係,然後在快取體上操作數據,複雜度都集在前者。而在一個普通JS物件進行增刪改查某屬性從來沒有難度,用戶怎麼也玩不出花招。從軟體設計原則來看,這也是最好的結果(吻合KISS原則與職責單一則)。
陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn