jQuery.Data源碼

来源:http://www.cnblogs.com/yaoyinglong/archive/2016/08/05/5738979.html
-Advertisement-
Play Games

jQuery.data的是jQuery的數據緩存系統。它的主要作用就是為普通對象或者DOM元素添加數據。 1 內部存儲原理 這個原理很簡單,原本要添加在DOM元素本身的數據,現在被集中的存儲在cache集合中。它們之間靠一個從1開始的數字鍵來聯繫著。這樣DOM元素就不會像以前那麼笨重了,更不會出現以... ...


jQuery.data的是jQuery的數據緩存系統。它的主要作用就是為普通對象或者DOM元素添加數據。

1 內部存儲原理

image

這個原理很簡單,原本要添加在DOM元素本身的數據,現在被集中的存儲在cache集合中。它們之間靠一個從1開始的數字鍵來聯繫著。這樣DOM元素就不會像以前那麼笨重了,更不會出現以前那種迴圈引用而引起的記憶體泄漏。現在DOM只需要保存好這個數字鍵值即可。這個屬性值被保存在DOM元素的一個屬性里,該屬性名是由jQuery.expando生成的。

2 Data構造函數

Object.defineProperty( this.cache = {}, 0, {
    get: function() {
        return {};
    }
});

首先來看Object.defineProperty函數,它的作用為:將屬性添加到對象,或修改現有屬性的特性。那我們先來看下ECMAScript5中的屬性。

2.1 ECMAScript5中的屬性

ECMAScript5中有兩種屬性:數據屬性和訪問器屬性。

2.1.1 數據屬性:

數據屬性包含一個數據值的位置,在這個位置可以讀取和寫入值。有4種特性用來限制其行為。

①[[configurable]]

能否通過delete刪除屬性,能否修改屬性的特性,能否把屬性改為訪問器屬性。預設為true。

var  obj={name:"abc"};
Object.defineProperty(obj,"name",{
    configurable:false
});
console.log(obj.name);
delete obj.name;
console.log(obj.name);
Object.defineProperty(obj,"name",{
    configurable:true
});
image

註意,一旦將 configurable 設為false,就再也設置不回去了。而且還會報錯。

②[[enumerable]]

在for-in迴圈是否能獲取到屬性。預設為true。ECMAScript規定,由程式員定義的屬性的該特性都為true。

③[[writable]]

能否修改屬性的值。預設為true。

var  obj={name:"abc"};
Object.defineProperty(obj,"name",{
    writable:false
});
console.log(obj.name);
obj.name="111";
console.log(obj.name);
image

④[[value]]

屬性值所在的地方。讀取屬性值的時候獲取的就是它,寫屬性值的時候也是往這裡寫。

var  obj={name:"abc"};
console.log(obj.name);
Object.defineProperty(obj,"name",{
    value:111
});
console.log(obj.name);
image

2.1.2訪問器屬性

訪問器屬性不包含實際的屬性值,它包含兩個函數(getter和setter)

①[[get]]

讀取屬性時調用的函數。預設為undefined

②[[set]]

寫入屬性時調用的函數。預設為undefined

var obj={};
Object.defineProperties(obj,{
    name:{
        get:function () {
            return obj["_name"];
        },
        set:function (name) {
            if(name != "C#"){
                obj["_name"]=name;
            }
        }
    },
    label:{
        get:function () {
            return "你不能改變我!!!";
        }
    }
});
obj.name="JavaScript";
console.log(obj.name);
obj.label="我偏要改變你!!!";
console.log(obj.label);
image

上面給cache的屬性0,只給了get屬性。所以不能為該屬性賦值。只能獲取。

this.expando = jQuery.expando + Math.random();

這個expando就是用來為DOM元素或者對象存儲在cache中的鍵的。即它將作為DOM元素的一個屬性被存儲這。

b4722170-1292-4c0e-bb79-8665339ad935

紅色畫框內的屬性名就是由上面的語句生成的,指明div1的屬性存儲在cache的1屬性中。

Data.uid = 1;
該屬性表示cache的屬性將從1開始自增。因為0已經被這個凍結的空JSON占用了,所以從1開始。我們下次再為某DOM元素添加屬性時,它將被保存在cache的2屬性中。

3 允許的添加屬性的元素

Data.accepts = function( owner ) {
    return owner.nodeType ?
        owner.nodeType === 1 || owner.nodeType === 9 : true;
};

這段代碼寫的非常幹練。表示如果owner是DOM元素則只有ELEMENT_NODE和DOCUMENT_NODE兩種元素能添加屬性,如果owner是一個對象,則都可以添加屬性。

4 原型屬性

4.1 key: function( owner ) {

if ( !Data.accepts( owner ) ) {
    return 0;
}

如果owner不能被添加data,則返回cache的第0個元素。

unlock = owner[ this.expando ];

這裡獲取的就是

b5699b61-ea45-4a9a-b45c-ebd3727f28af

如果能從owner中找到這個屬性,則說明以前為它添加過值,也就是說,它已經擁有了一個在cache中的key。

if ( !unlock ) {
    unlock = Data.uid++;
    try {
        descriptor[ this.expando ] = { value: unlock};
        Object.defineProperties( owner, descriptor );
    } catch ( e ) {
        descriptor[ this.expando ] = unlock;
        jQuery.extend( owner, descriptor );
    }
}

如果找不到,則說明是第一次為owner添加屬性,則要創建一個key。這個key就是在Data的uid的基礎上加1.Data的uid是一個靜態屬性。這樣就能記錄前一個元素的key是多少,這次的key又應該是多少。try塊裡面為owner(即DOM元素或者對象)添加它在cache中的索引。這裡會出現相容性的問題。在Android系統<4時會出現安全問題,因此jQuery使用extend靜態方法將descriptor擴展到owner上面。

if ( !this.cache[ unlock ] ) {
    this.cache[ unlock ] = {};
}

這裡為owner對應在cache中的key賦值一個Object。

這個的key方法的作用就是為DOM元素創建屬性,為cache中對應的key賦值(空對象)。

image

key返回的是這個unlock,即owner對象在cache中對應的key。

4.2 set: function( owner, data, value ) {

unlock = this.key( owner ),
cache = this.cache[ unlock ];
第一句不用解釋,第二句或者cachekey值。以上圖所示,則這裡的cache目前還是{},因為在此之前還沒有為div1添加過屬性。
if ( typeof data === "string" ) {
    cache[ data ] = value;

我們下麵的代碼走的就是這裡:

$("#div1").data("name","div1");

但是我們有很多屬性要設置的時候,

$("#div1").data({name:"div1",from:"0",to:"100"});

jQuery的處理方式是這樣的:

// Handle: [ owner, { properties } ] args
} else {
    // Fresh assignments by object are shallow copied
    if ( jQuery.isEmptyObject( cache ) ) {
        jQuery.extend( this.cache[ unlock ], data );
    // Otherwise, copy the properties one-by-one to the cache object
    } else {
        for ( prop in data ) {
            cache[ prop ] = data[ prop ];
        }
    }
}

其實這裡個人以為不用再判斷了,因為extend裡面也是用for迴圈將data的屬性擴展到cache中的。

4.3 get: function( owner, key ) {

var cache = this.cache[ this.key( owner ) ];
return key === undefined ? cache : cache[ key ];

獲取屬性,代碼非常簡單。如果key不存在則返回cache對象。

4.4 access: function( owner, key, value ) {

這裡對get和set方法的統一訪問。

4.5 remove: function( owner, key ) {

unlock = this.key( owner ),
cache = this.cache[ unlock ];

獲取owner的在cache中的數據對象,this.key返回的是owner在cache中的key。

if ( key === undefined ) {
    this.cache[ unlock ] = {};

如果不指定要刪除那個屬性的話,jQuery會刪除owner所有的數據屬性。

否則再判斷key是不是數組,

if ( jQuery.isArray( key ) ) {
    name = key.concat( key.map( jQuery.camelCase ) );
}
是數組的話,將key數組和用key的每一項轉駝峰後的數組合併,即:
$("#div1").remove(["one_key","two_key"]);

經過上面的代碼,key為變為["one_key","two_key","oneKey","twoKey"]。後面會將這4個屬性都刪除掉。

那如果key不是數組:

camel = jQuery.camelCase( key );
if ( key in cache ) {
    name = [ key, camel ];
} else {
    name = camel;
    name = name in cache ?
        [ name ] : ( name.match( core_rnotwhite ) || [] );
}

同樣先輸轉駝峰,然後判斷key是否存在,不存在則判斷key的駝峰形式是否存在,後面的正則表達式用於去除駝峰形式前後的空格。

15beac62-c2e7-453d-8cf6-a4814dbb9854

當這些情況過濾完之後,進行刪除操作:

i = name.length;
while ( i-- ) {
    delete cache[ name[ i ] ];
}

在while迴圈裡面使用delete進行刪除。

4.6 hasData: function( owner ) {

cache中是否擁有woner的數據對象。

4.7 discard: function( owner ) {

刪除cache中的woner的數據對象。

image

5 創建兩個私有的cache

data_user = new Data();
data_priv = new Data();
所以,我們在jQuery的外面不能直接拿到這個cache。因為它是jQuery的局部變數。data_user供開發人員使用,data_priv供jQuery內部使用。

6 創建對外介面(工具方法和原型方法)

由於Data構造器是jQuery私有的,我們在外面不能訪問到,所以前面的那些方法,我們也不能直接訪問,jQuery在這裡,給我們提供了一些介面。來操作data_user,為DOM元素和Object進行屬性操作。工具方法非常簡單隻是對data_user方法的封裝而已。我們主要看下原型方法。

jQuery.fn.extend({
    data: function( key, value ) {

設值和取值都會進入上面的方法。

這裡有這樣一個思想,如果是設值的時候,則給選集中所有的選項設值,如果獲取值的時候,只獲取第一個選項的值。

if ( key === undefined ) {
    if ( this.length ) {
        data = data_user.get( elem );
        if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
            attrs = elem.attributes;
            for ( ; i < attrs.length; i++ ) {
                name = attrs[ i ].name;
                if ( name.indexOf( "data-" ) === 0 ) {
                    name = jQuery.camelCase( name.slice(5) );
                    dataAttr( elem, name, data[ name ] );
                }
            }
            data_priv.set( elem, "hasDataAttrs", true );
        }
    }
    return data;
}

在第3行,已經取到cache中elem對應的屬性了,下麵jQuery由將elem的attributes里的所有屬性添加到elem的    。

hasDataAttrs屬性是我們自己添加,因為下麵這段代碼只需要執行一次即可。代碼第6行,獲取elem所有的屬性;

a7b4953a-c10f-4d0a-a507-6542fe126ee4

這個NamedNodeMap類型,我們平時很少直接用它,它和NodeList,HTMLCollection一樣都是“動態”的。NamedNodeMap集合中的每一項都是Attr類型,Attr對象有3個屬性:name,value和specified。

在下麵的for迴圈裡面,就檢測這個name屬性中是否含有"data-"首碼。有的話就去掉它,並且將剩餘部分轉為駝峰形式。

如果key不存在,則表示獲取選集中第一個選項的所有屬性值。elem表示第一個選項。

if ( typeof key === "object" ) {
    return this.each(function() {
        data_user.set( this, key );
    });
}
對應這種形式:
$("#div1").remove(["one_key","two_key"]);

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • QWhatsThis,為任何widget提供簡單的描述,回答"What's This?"這個問題。 示例:(在MainWindow下運行) 定義全局變數 QAction *newAct; QMenu *fileMenu; void MainWindow::creatActions() { newAc ...
  • 開發和測試向來就是一對冤家,再加上產品再添一把火,不吵才是奇跡呢。所以一般我們開發的時候儘可能的減少一些小的失誤,比如說獲取的數值為空,判定條件不充分、當然還有面對測試人員測試時將軟體暴力測試,出現一些網路解析數據的延遲有時也是無法避免的。可是這些確實是一些困擾。但是為了提高開發的效率,犯這些小錯誤 ...
  • 本文為原創文章,轉載請註明出處,謝謝 數據的發佈與訂閱 1、應用 服務端監聽數據改變,客戶端創建/更新節點數據,客戶端提供數據,服務端處理 2、原理 客戶端監控節點數據改變事件(例如配置信息,下圖的config節點),啟動時在伺服器節點下創建臨時節點(圖中servers下節點) 服務端監聽工作伺服器 ...
  • 1.定義 定義對象間一種一對多的依賴關係,使得當每一個對象改變狀態,則所有依賴於它的對象都會得到通知並自動更新。 2.類圖 3.代碼示例 我們定義一個場景:熱水壺在燒開水,小孩和媽媽都關註燒開水的過程,各自有其處理方法。用while死迴圈一直輪詢雖然可以實現這樣的場景,但性能上讓人無法接受。 為方便 ...
  • ajax簡介 AJAX即“Asynchronous Javascript And XML”(非同步JavaScript和XML),是指一種創建互動式網頁應用的網頁開發技術。Ajax不是一種新的編程語言,而是使用現有標準的新方法。AJAX可以在不重新載入整個頁面的情況下,與伺服器交換數據。這種非同步交互的 ...
  • soChange一款多很經典的幻燈片的jQuery插件。 實例預覽 引入文件 複製 使用方法 複製 複製 soChange參數 複製 soChange 即 simple object change ,對象切換插件,充分發揮css樣式的靈活性,不僅僅適用於圖片相冊,也適用於選項卡或文字類型的切換,以上 ...
  • js代碼: 游戲的對象 ,食物,蛇 ,游戲控制思路如下 (完整代碼在https://github.com/774044859yf/ObjectSnakeGame下載) var snake = { ...
  • [1]參數預設值 [2]rest參數 [3]擴展運算符 [4]箭頭函數 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...