這一章主要講的是jQuery的緩存系統的歷史發展,以及他自己的框架的緩存系統的實現。都是源碼解析。
我就挑幾個重點講下:
(1)jQuery的緩存機制的原理
jQuery的緩存機制實現的原理是在元素中添加自定義屬性,然后把這個自定義屬性賦值為uid,而這個uid就在jQuery的cache對象中的一個屬性(唯一的),這個唯一的屬性其實是一個對象,這個對象里面存儲的就是你給這個元素添加的數據。
舉個例子:
<input id="chaojidan" name="chaojidan" >
<input id="chaojidan1" name="chaojidan1" >
$("#chaojidan").data({name:"chaojidan",age:"25"});
執行這個語句后,
<input id="chaojidan" name="chaojidan" 自定義屬性(jQuery版本+隨機數) = uid(1,一個從0開始累加的整數)>
在jQuery中,$.cache = {1: { name:"chaojidan",age:"25" }}
當給第二個input添加數據時,
$("#chaojidan1").data({name:"chaojidan1",age:"26"});
執行這個語句后,
<input id="chaojidan1" name="chaojidan1" 自定義屬性(jQuery版本+隨機數) = uid(2,一個從0開始累加的整數)>
在jQuery中,$.cache = { 1: { name:"chaojidan",age:"25" }, 2: { name:"chaojidan1",age:"26" }}
取數據時,會先在元素中查找自定義屬性的值(uid),然后再去$.cache對象中查找uid,得到之前存儲的數據,最后通過需要取得什么數據的key值,返回value值。
(2)兼容性問題
在舊版本IE中,元素節點(object,embed,applet)只是COM的包裝,一旦引入資源后,它就會變成那種資源的實例。一旦這資源是由VB等語言編寫,由於VM有嚴格的訪問控制,不能隨便給對象添加新屬性和方法,就會出現無法使用jQuery緩存系統。
HTML5新增了一種data-*的緩存機制,當用戶在元素上設置了data-開頭的屬性時,它們的值會保存在元素節點的dataset對象上。但是它只支持字符串(以防循環引用)。
這里我說下循環引用的實例:
input.moneySet = { fangzi:"shenzhen",ele:input}
元素節點input有一個自定義屬性是moneySet,它的值是一個對象,如果是一個字符串,永遠都不會循環引用。由於是一個對象,對象中有一個ele屬性,這個屬性剛好又指向input元素。這時就出現了循環引用的狀態。
(3)新一代的jQuery緩存機制實現原理
它是通過對valueOf方法重寫,並通過Object.defineProperty方法操作實現的,這套緩存系統不支持IE8以及以下版本瀏覽器。
實現原理:對每一個實例(調用jQuery緩存系統中data方法的任何東西),調用valueOf方法,並傳入jQuery中的Data類,如果返回object,就證明valueOf方法沒重寫,我們就通過Object.defineProperty重寫它的valueOf方法。如果返回string,則已經被重寫了,就不用再次重寫。
Object.defineProperty(目標對象,要定義的屬性或者方法名,目標屬性所擁有的特性)
Object.defineProperty(目標對象,"valueOf",{ value:function(){ return value1} //writable ,configurable, enumerable內部屬性 ),這句代碼的意思就是,目標元素的valueOf方法被重寫了,它的valueOf方法的值是value1,同時還可以設置valueOf是否可以被遍歷,被重寫,被重新定義。默認情況下是不能的。如果你在里面寫了writable:true,那么目標對象的valueOf就是可重寫的。
以上方法,還有內部屬性,在js高級程序設計里面有詳解。但是實際項目中用的比較少,作為了解就行。如果是開發移動端,還是推薦去精讀的。
(4)ECMAScript6新特性創建的緩存系統
之前的緩存系統都是通過唯一的一個ID,來建立目標對象(元素節點)與緩存體(緩存系統Cache)之間的連接。而ES6中有一個新的集合對象WeakMap。
WeakMap是個什么樣的對象呢,平時,我們的js對象,鍵名name只能是字符串,鍵值key任意。我們可以通過for in循環遍歷它的所有鍵值對。而WeakMap的鍵名name只能為一個非null的對象,鍵值key任意。我們無法通過for in循環遍歷它里面的鍵值對,讀寫或刪除只能對它暴露的接口進行。它目前只有四個方法:set,get,has,delete。
舉個例子:
var map = new WeakMap();
el = document.body;
map.set(el,{data:{}}); //設置鍵值對
var value = map.get(el); //讀取目標值
console.log(value) //{data:{}}
console.log(map.has(el)) //是否有此鍵名name,這里是true
map.delete(el) //刪除鍵值對。如果作為鍵名的對象el被刪除,那么它對應的緩存體(el:{data:{}})會自動被清除出WeakMap對象。
因此通過此對象很容易實現緩存系統。大家都知道,我們的目標元素不過是元素節點,document對象,window對象,完全可以做為WeakMap的鍵名。我們可以把緩存倉庫改成一個WeakMap實例。我們不再需要用唯一的id來作為橋梁關聯兩者。只需要map.set方法就可以建立關聯了。判定目標元素是否關聯着緩存體,只需要用map.has方法。刪除緩存體用map.delete就可以了。非常方便。但是兼容性就不容樂觀了。
這是新技術,了解就行。
總結:
數據緩存其實就是在目標元素與緩存體之間建立一對一的關系,然后在緩存體上操作數據。
加油!