jQuery資料緩衝功能的解析及簡單實現

來源:互聯網
上載者:User

前言

對於jQuery的資料緩衝,相信大家都不會陌生,jQuery緩衝系統不僅運用於DOM元素動畫、事件等都有用到這個緩衝系統。所以在平時實際應用中, 我們經常需要給元素緩衝一些資料,並且這些資料往往和DOM元素緊密相關。由於DOM元素(節點)也是對象, 所以我們可以直接擴充DOM元素的屬性,但是如果給DOM元素添加自訂的屬性和過多的資料可能會引起記憶體流失,所以應該要盡量避免這樣做。 因此更好的解決方案是使用一種低耦合的方式讓DOM和快取資料能夠聯絡起來。

 另外:對於jQuery.data和jQuery.removeData靜態方法、以及基於這兩個方法的原型擴充方法的介紹和用法就不多說了,可以查看官方API文檔。

實現思路

jQuery提供了一套靈活和強大的緩衝方法:

(1)先在jQuery內部建立一個cache對象{}, 來儲存快取資料。 然後往需要進行緩衝的DOM節點上擴充一個值為expando的屬性, 這裡是”jQuery” + (new Date).getTime()。 註:expando的值等於”jQuery”+目前時間, 元素本身具有這種屬性的可能性很少,所以可以忽略衝突。

(2)接著把每個節點的dom[expando]的值都設為一個自增的變數id,保持全域唯一性。 這個id的值就作為cache的key用來關聯DOM節點和資料。也就是說cache[id]就取到了這個節點上的所有緩衝,即id就好比是開啟一個房間(DOM節點)的鑰匙。 而每個元素的所有緩衝都被放到了一個map映射裡面,這樣可以同時緩衝多個資料。

(3)所以cache對象結構應該像下面這樣:

var cache = {    "uuid1": { // DOM節點1快取資料,"uuid1"相當於dom1[expando]        "name1": value1,         "name2": value2    },    "uuid2": { // DOM節點2快取資料,“uuid2"相當於dom2[expando]        "name1": value1,         "name2": value2    }    // ......};

每個uuid對應一個elem快取資料,每個緩衝對象是可以由多個name/value(名值對)對組成的,而value是可以是任何資料類型的。

 

簡單類比實現

根據以上思路,就可以簡單實現下jQuery.data和jQuery.removeDate的功能了:

(function(window, undefined) {    var cacheData = {}, // 用來儲存資料的對象        win = window, // 把window緩衝給一個變數        uuid = 0,        // 聲明隨機數(8位)        // 注意+new Date()產生的隨機數是Number類型,與一個Null 字元串聯接後(或使用toString方法轉型後)變成字串,才可使用slice方法。        expando = "cacheData" + (+new Date() + "").slice(-8);        // (+new Date()).toString().slice(-8)等價於expando    // 寫入緩衝    var data = function(elem, name, value) {        // 或使用原生方法驗證字串Object.prototype.toString.call(elem) === "[object String]"        // 如果elem為字串        if (typeof elem === "string") {            // 如果傳入name參數,則為寫入緩衝            if (name !== undefined) {                cacheData[elem] = name;            }            // 返回快取資料            return cacheData[elem];        // 如果elem為DOM節點        } else if (typeof elem === "object") {            var id,                thisCache;            // 如果elem不存在expando屬性,則添加一個expando屬性(第一次給元素設定緩衝),否則直接擷取已有的expando和id值            if (!elem[expando]) {                id = elem[expando] = ++uuid;                thisCache = cacheData[id] = {};            } else {                id = elem[expando];                thisCache = cacheData[id];            }            // 把一個隨機數作為當前緩衝對象的一個屬性,利用該隨機數就能找到該緩衝對象            if (!thisCache[expando]) {                thisCache[expando] = {};            }            if (value !== undefined) {                // 將資料存到緩衝對象中                thisCache[expando][name] = value;            }            // 返回DOM元素儲存的資料            return thisCache[expando][name];        }    };    // 刪除緩衝    var removeData = function(elem, name) {        // 如果elem為字串,則直接刪除該屬性值        if (typeof elem === "string") {            delete cacheData[elem];        // 如果key為DOM節點        } else if (typeof elem === "object") {            // 如果elem不存在expando屬性,則終止執行,不用刪除緩衝            if (!elem[expando]) {                return;            }            // 檢測對象是否為空白            var isEmptyObject = function(obj) {                var name;                for (name in obj) {                    return false;                }                return true;            }            removeAttr = function() {                try {                    // IE8即標準瀏覽器可以直接使用delete來刪除屬性                    delete elem[expando];                } catch (e) {                    // IE6/IE7使用removeAttribute方法來刪除屬性                    elem.removeAttribute(expando);                }            },            id = elem[expando];            if (name) {                // 只刪除指定的資料                delete cacheData[id][expando][name];                // 如果是Null 物件,id所對應的資料對象全部刪除                if (isEmptyObject(cacheData[id][expando])) {                    delete cacheData[id];                    removeAttr();                }            } else {                // 刪除DOM元素存到緩衝中的所有資料                delete cacheData[id];                removeAttr();            }        }    };    // 把data和removeData掛在window全域對象下,這樣在外部也能訪問到這兩個函數    win.expando = expando;    win.data = data;    win.removeData = removeData;})(window, undefined);

例子:

HTML結構:

<div id="demo" style="height: 100px; width: 100px; background: #ccc; color: #fff; margin: 20px; text-align: center; line-height: 100px;">    demo</div>

js代碼:

window.onload = function() {    // 測試    var demo = document.getElementById("demo");    // 寫入緩衝    data(demo, "myName", "hcy");    console.log(data(demo, "myName")); // hcy    data(demo, "myBlog", "http://www.cnblogs.com/cyStyle");    console.log(data(demo, "myBlog")); // http://www.cnblogs.com/cyStyle    // 刪除DOM元素的某個緩衝值    removeData(demo, "myBlog");    console.log(data(demo, "myBlog")); // undefined    console.log(data(demo, "myName")); // hcy    console.log(demo[expando]); // 1    // 刪除DOM元素    removeData(demo);    console.log(demo[expando]); // undefined};

firefox下例子結果:

對於上述例子實現jQuery的簡單緩衝系統:先給該DOM元素添加一個隨機產生的屬性expando,這個屬性用來存放訪問快取資料的id值,就好比DOM元素都有一把開啟緩衝保險箱的鑰匙,只要有了鑰匙就可以隨時開啟緩衝保險箱。 將本來存放到DOM元素中的資料都轉到了緩衝中,而DOM元素本身只要儲存一個簡單的屬性就可以了,這樣就可以將由DOM元素引起的記憶體流失(具體會發生什麼狀況不知道,大家都這麼說~)的風險規避到最小。

 

結語

 糊裡糊塗地又到了最後,有一些術語或解釋上可能存在偏差,望各位童鞋指正和給出一些建議;另外,從理論上講, data和removeData方法可以用於任何對象的緩衝, 不過如果運用於本機物件或window對象, 會存在記憶體泄露、循環參考等問題(^_^從網上看到的), 所以一般還是用於DOM節點比較適合,還可以結合事件、動畫對DOM節點進行快取資料的操作。ps:cache真的很重要!需要慢慢體會~

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.