jQuery 3.0 的 Data 淺析

来源:http://www.cnblogs.com/snandy/archive/2016/06/14/5578467.html
-Advertisement-
Play Games

jQuery 3.0 在6月9日正式發佈了,3.0 也被稱為下一代的 jQuery 。這個版本從14年10月開始,其中發佈過一次beta 版(2016/1/14,)和候選版(2016/05/20)。一路走來,頗為不易。 文章目錄 一、Data淺析 jQuery 3.0 中的 Data 是內部使用的, ...


jQuery 3.0 在6月9日正式發佈了,3.0 也被稱為下一代的 jQuery 。這個版本從14年10月開始,其中發佈過一次beta 版(2016/1/14,)和候選版(2016/05/20)。一路走來,頗為不易。

 

文章目錄

  1. Data淺析
  2. Data在jQuery內部的使用
  3. 1.x.x 和 2.x.x 的比較

 

一、Data淺析

jQuery 3.0 中的 Data 是內部使用的,定義為一個“類”。一共用它創建了兩個對象,dataPriv 和 dataUser。Data 有 1 個對象屬性(expando)和類屬性(uid),有 6 個方法,如下

 

下麵分別解讀

 

1、Data.uid

這是一個從 1 開始用來自增的數字。

 

2、expando

由 jQuery.expando 和 uid 組合而成,它用來作為元素(如DOM元素)的key,是唯一的。jQuery.expando 的生成如下

jQuery.expando = "jQuery" + ( version + Math.random() ).replace( /\D/g, "" )

 

即 'jQuery' + (版本號 + 隨機數),然後把非數字的都去掉,比如

"jQuery" + "3.0.0" + 0.129896303388626 == "jQuery3.0.00.129896303388626"

 

去掉非數字變為

"jQuery30009423638425146147"

  

jQuery 3.0 內部變數 dataPriv 和 dataUser 生成 expando 如下

jQuery 300 024727210109188635 1
jQuery 300 024727210109188635 2

第三部分是隨機數,每次刷新都會變,其它部分的不變。

 

3、cache

cache 方法會給 owner 上綁定一個對象作為存儲,owner 必須滿足 acceptData 的,cache 會以 this.expando 為線索 key。
owner 有兩種,一中是DOM元素(nodeType為1和9),另一種則是普通的JS對象。諸如 文本節點(nodeType=3)、註釋節點(nodeType=8) 一律不添加。

 

acceptData 的定義如下

var acceptData = function( owner ) {

	// Accepts only:
	//  - Node
	//    - Node.ELEMENT_NODE
	//    - Node.DOCUMENT_NODE
	//  - Object
	//    - Any
	/* jshint -W018 */
	return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
};

acceptData 在 3.0 中一共有 3 處使用,分別為

  1. Data 類的 cache 方法,為私有方法不提供給程式員使用。
  2. $.cleanData 方法,清空元素關聯的所有緩存數據。為公開方法,但很少使用。該方法被用在 empty、html、replaceWith、remove 方法中。
  3. $().trigger 方法,主動派發事件,為公開方法。

 

如果是 DOM 元素,則直接使用點操作符賦值,如果是普通 JS 對象則使用 ES5 的 Object.defineProperty 方法,這也是 jQuery 3.0 會使用新 API 的體現。

// If it is a node unlikely to be stringify-ed or looped over
// use plain assignment
if ( owner.nodeType ) {
	owner[ this.expando ] = value;

// Otherwise secure it in a non-enumerable property
// configurable must be true to allow the property to be
// deleted when data is removed
} else {
	Object.defineProperty( owner, this.expando, {
		value: value,
		configurable: true
	} );
}

 

轉換成如下代碼

elem['jQuery3000247272101091886351'] = dataObj;

var person = {name: 'John', age: 30};
Object.defineProperty( person, 'jQuery3000247272101091886351', {
	value: dataObj,
	configurable: true
} );

 

cache 方法就是這樣,傳入 owner,只有第一次會 set ,返回 value (一個空對象),之後取到 value 後直接返回。

源碼

cache: function( owner ) {

	// Check if the owner object already has a cache
	var value = owner[ this.expando ];

	// If not, create one
	if ( !value ) {
		value = {};

		// We can accept data for non-element nodes in modern browsers,
		// but we should not, see #8335.
		// Always return an empty object.
		if ( acceptData( owner ) ) {

			// If it is a node unlikely to be stringify-ed or looped over
			// use plain assignment
			if ( owner.nodeType ) {
				owner[ this.expando ] = value;

			// Otherwise secure it in a non-enumerable property
			// configurable must be true to allow the property to be
			// deleted when data is removed
			} else {
				Object.defineProperty( owner, this.expando, {
					value: value,
					configurable: true
				} );
			}
		}
	}

	return value;
},

 

4、set

上面的 cache 方法為 owner 建立一個以 expando 為 key 的空對象,後面所有的方法都圍繞這個空對象來展開,這個空對象就被稱為緩存對象,後面所有的數據都添加到它上面。set 就是給這個對象來添磚加瓦,set 每次都是先取回 cache ,再給其添加新的屬性及數據。如果 data 是字元串,則以它為 key 添加,如果是對象,則遍歷它添加。只需註意一點,橫線連接符內部會被轉成駝峰格式,這也是為了對 H5 data-xxx 的相容 。

源碼

set: function( owner, data, value ) {
	var prop,
		cache = this.cache( owner );

	// Handle: [ owner, key, value ] args
	// Always use camelCase key (gh-2257)
	if ( typeof data === "string" ) {
		cache[ jQuery.camelCase( data ) ] = value;

	// Handle: [ owner, { properties } ] args
	} else {

		// Copy the properties one-by-one to the cache object
		for ( prop in data ) {
			cache[ jQuery.camelCase( prop ) ] = data[ prop ];
		}
	}
	return cache;
},

  

 

5、get

get 簡單至極,傳 key 則從 cache 上取回該 key 的值,無則取回整個 cache。

源碼

get: function( owner, key ) {
	return key === undefined ?
		this.cache( owner ) :

		// Always use camelCase key (gh-2257)
		owner[ this.expando ] && owner[ this.expando ][ jQuery.camelCase( key ) ];
},

  

6、access

這個方法即時 getter,也是 setter,如此而已。

getter 條件

  1. key 是 undefined,這時取整個 cache
  2. key 是字元串且value 是undefined,這是取指定 key 的值


setter 條件

  1. owner、key、value 這三個參數都傳

源碼

access: function( owner, key, value ) {

	// In cases where either:
	//
	//   1. No key was specified
	//   2. A string key was specified, but no value provided
	//
	// Take the "read" path and allow the get method to determine
	// which value to return, respectively either:
	//
	//   1. The entire cache object
	//   2. The data stored at the key
	//
	if ( key === undefined ||
			( ( key && typeof key === "string" ) && value === undefined ) ) {

		return this.get( owner, key );
	}

	// When the key is not a string, or both a key and value
	// are specified, set or extend (existing objects) with either:
	//
	//   1. An object of properties
	//   2. A key and value
	//
	this.set( owner, key, value );

	// Since the "set" path can have two possible entry points
	// return the expected data based on which path was taken[*]
	return value !== undefined ? value : key;
},

 

7、remove

清空綁定元素(owner)上面的緩存對象,依然需要先通過 this.expando 拿到 cache,如果傳了 key 則刪除指定key的值(key自身也被刪除)。
當然 jQuery API 保持已有的方便性,key 可以為一個數組,這樣可以批量刪除多個 key。如果 key 沒傳則將整個 cache 刪除,這裡區分了 DOM 和普通 JS 對象,DOM 對象使用undefined賦值,JS 對象則使用 delete。

源碼

remove: function( owner, key ) {
	var i,
		cache = owner[ this.expando ];

	if ( cache === undefined ) {
		return;
	}

	if ( key !== undefined ) {

		// Support array or space separated string of keys
		if ( jQuery.isArray( key ) ) {

			// If key is an array of keys...
			// We always set camelCase keys, so remove that.
			key = key.map( jQuery.camelCase );
		} else {
			key = jQuery.camelCase( key );

			// If a key with the spaces exists, use it.
			// Otherwise, create an array by matching non-whitespace
			key = key in cache ?
				[ key ] :
				( key.match( rnotwhite ) || [] );
		}

		i = key.length;

		while ( i-- ) {
			delete cache[ key[ i ] ];
		}
	}

	// Remove the expando if there's no more data
	if ( key === undefined || jQuery.isEmptyObject( cache ) ) {

		// Support: Chrome <=35 - 45
		// Webkit & Blink performance suffers when deleting properties
		// from DOM nodes, so set to undefined instead
		// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
		if ( owner.nodeType ) {
			owner[ this.expando ] = undefined;
		} else {
			delete owner[ this.expando ];
		}
	}
}, 

 

8、hasData

用來判斷 owner 上是否有緩存數據,返回 true 或 false。

源碼

hasData: function( owner ) {
	var cache = owner[ this.expando ];
	return cache !== undefined && !jQuery.isEmptyObject( cache );
}

 

二、Data在jQuery內部的使用

以上解讀完了 Data 的所有方法,上面也提到 Data 類是在 jQuery 內部使用的,一共創建了它的兩個對象:dataPriv 和 dataUser。

這兩個對象在 3.0.0 中有著明確的分工,dataPriv 可以猜測到是 “data” 和 “private” 兩個單詞的組合後簡寫。即 dataPriv 是私有的用來服務 jQuery 內部方法,dataUser 用來服務那些公開給用戶使用的方法。

 

下麵看下這兩個對象分佈在哪些模塊中使用。

 

完整版點擊展開可查看

dataPriv
	
	公共
		$.hasData
		$.cleanData
		cloneCopyEvent

	隊列
		$().queue
		$()._queueHooks
		$().promise

	動畫
		$().animate
		$().stop
		$().finish
		showHide

	事件
		$.event.add
		$.event.remove
		$.event.dispatch
		$.event.trigger
	
	其它
		setGlobalEval
		domManip
		defaultPrefilter
		$().toggleClass

dataUser

	公共
		$.hasData
		$.cleanData
		cloneCopyEvent

	數據緩存
		$.data
		$.removeData
		$().data
		$().removeData

	其它
		dataAttr

 

以上可以看到,除了“公共”,DataPriv 用在了 jQuery 的 隊列、動畫、事件等模塊;dataUser 用在了數據緩存及dataAttr模塊。

 

“公共” 是指這三個方法內都用到了 dataPriv 和 dataUser

 

$.hasData(elem)

用來判斷 elem 上是否綁定了相關的數據緩存,返回 true 和false,只有 dataPriv 和 dataUser 上都沒有才返回 false

源碼

hasData: function( elem ) {
return dataUser.hasData( elem ) || dataPriv.hasData( elem );
},

 

$.cleanData(elems)

清空 elem 上綁定的所有數據緩存,理所當然的需要同時清空 dataPriv 和 dataUser 上的。
註意:雖然這個方法在公開暴露在了 $ 上, 但官網API上卻沒有該方法的介紹。另使用不當會造成嚴重後果,比如綁定了事件後(.on),調用該方法,綁定的事件將全部失效。因為會清空 dataPriv 內的所有數據。


cloneCopyEvent(src, dest)

這是一個內部方法,$.clone 會使用到它。克隆元素時除了會克隆node節點外,綁定在node上的數據也會被克隆過去。比如

var cloneNode = $.clone(elem);

把 elem 克隆給 cloneNode,此時 elem 上添加的事件 cloneNode 上也會有。

 

三、1.x.x 和 2.x.x 的比較

jQuery 1.x 系列 和 2.x 系列的版本對 數據緩存模塊的實現差異還是很大的。大家可以對比我11年的這篇文章

 

1. 緩存的數據結構

1.x (直到1.11.2) 緩存都是存儲在 jQuery.cache 上的,2.x(包括3.x) 則使用了一個內部類 Data 做緩存,其主要用到了兩個對象 dataPriv 和 dataUser。很明顯 2.x 做的更好,它所有的緩存數據都是私有的,不會存在被誤寫的風險,而 1.x 的 jQuery.cache 是公開的,如果被誤寫(比如某個同學想當然的給$上添加一個cache對象)後果不堪設想。


2. jQuery._data

看到這個下劃線就知道是私有的(約定式),在 1.x 中是僅在內部使用的,不提供給開發者。以 1.11.2 示例、這個方法被事件模塊、隊列模塊、動畫模塊、setGlobalEval、cloneCopyEvent、fixCloneNodeIssues、domManip、showHide、defaultPrefilter、toggleClass 使用。3.x 則使用 dataPriv 和 dataUser 替代,大家可以對比看看。

(2/3).x 相比 1.x 明顯更優,dataPriv 和 dataUser 是真正的私有的(封裝的更好,更安全),比起 1.x 約定式的私有 jQuery._data。雖然 3.0.0 還保守的相容了 jQuery._data,相信過不了多久的後續版本就會剔除。


3. 重構

1.x 以 $._data 為中心,以它來輔助實現其它 API, (2/3).x 以 dataPriv/dataUser 為中心來實現。(2/3).x 將代碼重構後提取出了 Data 類,更加清晰。

 

 

相關:

http://naotu.baidu.com/file/c287195ae96011f7511571a4280042c7?token=ddb7c115786ff90f

http://naotu.baidu.com/file/186bfe75ebe2878fa4d70856f0f33672?token=c8b112b939e0f65c

http://www.cnblogs.com/snandy/archive/2011/06/10/2077298.html

http://www.cnblogs.com/snandy/p/4650599.html


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

-Advertisement-
Play Games
更多相關文章
  • 本文是【Knockout.js 學習體驗之旅】系列文章的第3篇,所有demo均基於目前knockout.js的最新版本(3.4.0)。小茄才識有限,文中若有不當之處,還望大家指出。 目錄: 【Knockout.js 學習體驗之旅】(1)ko初體驗 【Knockout.js 學習體驗之旅】(2)花式捆 ...
  • 構造函數覆蓋模式,使用new操作符調用該函數的行為就如以函數調用它的行為一樣。這能工作完全利益於js允許new表達式的結果可以被構造函數中的顯式return語句所覆蓋。當User函數返回self對象時,new表達式的結果就變為self對象。該self對象可能是另一個綁定到this的對象。 防範誤用構... ...
  • __proto__屬性很特殊,它提供了Object.getPrototypeOf方法所不具備的額外能力,即修改對象原型鏈接的能力。 避免修改__proto__屬性的最明顯的原因是可移植性的問題。並不是所有的平臺都支持修改對象原型的特性,所以無法編寫可移植的代碼。 避免修改__proto__屬性的另一 ...
  • 前幾天,舍友去某互聯網公司面前端研發工程師。回來後,他就跟我們聊了下麵試官給他出的題。其中,有一道題是“如何實現iframe高度的自適應?”。好吧,我承認,我聽到iframe這個詞的第一反應就是:這個東西性能差、搜索引擎不友好等等。由於這樣的偏見,還真沒有好好研究一下iframe。其實,iframe ...
  • every():對數組每一項都遍歷,然後每一項都符合要求的話則返回true,否則就返回false some():對數組每一項都遍歷,其中有一項符合要求則返回true,否則返回false filter():對數組的每一項都遍歷,返回其中要求的元素組成的數組 map():對數組每一項都遍歷並且運行給定的 ...
  • 之前的項目一直採用grunt來構建,然後用requirejs做模塊化,requirejs官方有提供grunt的插件來做壓縮合併。現在的項目切到了gulp,模塊化用起了seajs,自然而然地也想到了模塊合併壓縮的問題。然後一開始在解決這個問題的時候,並不是很順利,在npm上並沒有那種特別流行的專門用來... ...
  • 將近20年前,Javascript誕生的時候,只是一種簡單的網頁腳本語言。如果你忘了填寫用戶名,它就跳出一個警告。 如今,它變得幾乎無所不能,從前端到後端,有著各種匪夷所思的用途。程式員用它完成越來越龐大的項目。 Javascript代碼的複雜度也直線上升。單個網頁包含10000行Javascrip ...
  • 無論是提示框還是導航欄都能看到如上圖所示的帶有箭頭的框框,這種箭頭可以通過背景圖片或者是css來實現,本文介紹三種通過css實現帶箭頭的提示框。 通過border屬性思路:兩個三角形,通過定位使兩個三角形相差1px作為邊框。 CSS3 transfrom思路:先做一個兩條邊相同顏色的正方形,然後旋轉 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...