淺析jQuery 3.0中的Data
jQuery 3.0 在6月9日正式發(fā)布了,3.0 也被稱為下一代的 jQuery。這個(gè)版本從14年10月開始,其中發(fā)布過一次beta 版(2016/1/14,)和候選版(2016/05/20)。一路走來,頗為不易。
一、Data淺析
jQuery 3.0 中的 Data 是內(nèi)部使用的,定義為一個(gè)“類”。一共用它創(chuàng)建了兩個(gè)對(duì)象,dataPriv 和 dataUser。Data 有 1 個(gè)對(duì)象屬性(expando)和類屬性(uid),有 6 個(gè)方法,如下

下面分別解讀
1、Data.uid
這是一個(gè)從 1 開始用來自增的數(shù)字。
2、expando
由 jQuery.expando 和 uid 組合而成,它用來作為元素(如DOM元素)的key,是唯一的。jQuery.expando 的生成如下
jQuery.expando = "jQuery" + ( version + Math.random() ).replace( /\D/g, "" )
即 'jQuery' + (版本號(hào) + 隨機(jī)數(shù)),然后把非數(shù)字的都去掉,比如
"jQuery" + ".." + . == "jQuery..."
去掉非數(shù)字變?yōu)?/p>
jQuery30009423638425146147"
jQuery 3.0 內(nèi)部變量 dataPriv 和 dataUser 生成 expando 如下
jQuery 300 024727210109188635 1 jQuery 300 024727210109188635 2
第三部分是隨機(jī)數(shù),每次刷新都會(huì)變,其它部分的不變。
3、cache
cache 方法會(huì)給 owner 上綁定一個(gè)對(duì)象作為存儲(chǔ),owner 必須滿足 acceptData 的,cache 會(huì)以 this.expando 為線索 key。
owner 有兩種,一中是DOM元素(nodeType為1和9),另一種則是普通的JS對(duì)象。諸如 文本節(jié)點(diǎn)(nodeType=3)、注釋節(jié)點(diǎn)(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 處使用,分別為
Data 類的 cache 方法,為私有方法不提供給程序員使用。$.cleanData 方法,清空元素關(guān)聯(lián)的所有緩存數(shù)據(jù)。為公開方法,但很少使用。該方法被用在 empty、html、replaceWith、remove 方法中。$().trigger 方法,主動(dòng)派發(fā)事件,為公開方法。
如果是 DOM 元素,則直接使用點(diǎn)操作符賦值,如果是普通 JS 對(duì)象則使用 ES5 的 Object.defineProperty 方法,這也是 jQuery 3.0 會(huì)使用新 API 的體現(xiàn)。
// 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
} );
}
轉(zhuǎn)換成如下代碼
elem['jQuery3000247272101091886351'] = dataObj;
var person = {name: 'John', age: 30};
Object.defineProperty( person, 'jQuery3000247272101091886351', {
value: dataObj,
configurable: true
} );
cache 方法就是這樣,傳入 owner,只有第一次會(huì) set ,返回 value (一個(gè)空對(duì)象),之后取到 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 建立一個(gè)以 expando 為 key 的空對(duì)象,后面所有的方法都圍繞這個(gè)空對(duì)象來展開,這個(gè)空對(duì)象就被稱為緩存對(duì)象,后面所有的數(shù)據(jù)都添加到它上面。set 就是給這個(gè)對(duì)象來添磚加瓦,set 每次都是先取回 cache ,再給其添加新的屬性及數(shù)據(jù)。如果 data 是字符串,則以它為 key 添加,如果是對(duì)象,則遍歷它添加。只需注意一點(diǎn),橫線連接符內(nèi)部會(huì)被轉(zhuǎn)成駝峰格式,這也是為了對(duì) 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 的值,無則取回整個(gè) 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
這個(gè)方法即時(shí) getter,也是 setter,如此而已。
getter 條件
key 是 undefined,這時(shí)取整個(gè) cachekey 是字符串且value 是undefined,這是取指定 key 的值
setter 條件
owner、key、value 這三個(gè)參數(shù)都傳
源碼
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)上面的緩存對(duì)象,依然需要先通過 this.expando 拿到 cache,如果傳了 key 則刪除指定key的值(key自身也被刪除)。
當(dāng)然 jQuery API 保持已有的方便性,key 可以為一個(gè)數(shù)組,這樣可以批量刪除多個(gè) key。如果 key 沒傳則將整個(gè) cache 刪除,這里區(qū)分了 DOM 和普通 JS 對(duì)象,DOM 對(duì)象使用undefined賦值,JS 對(duì)象則使用 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 上是否有緩存數(shù)據(jù),返回 true 或 false。
源碼
hasData: function( owner ) {
var cache = owner[ this.expando ];
return cache !== undefined && !jQuery.isEmptyObject( cache );
}
二、Data在jQuery內(nèi)部的使用
以上解讀完了 Data 的所有方法,上面也提到 Data 類是在 jQuery 內(nèi)部使用的,一共創(chuàng)建了它的兩個(gè)對(duì)象:dataPriv 和 dataUser。
這兩個(gè)對(duì)象在 3.0.0 中有著明確的分工,dataPriv 可以猜測到是 “data” 和 “private” 兩個(gè)單詞的組合后簡寫。即 dataPriv 是私有的用來服務(wù) jQuery 內(nèi)部方法,dataUser 用來服務(wù)那些公開給用戶使用的方法。
下面看下這兩個(gè)對(duì)象分布在哪些模塊中使用。

完整版點(diǎn)擊展開可查看
dataPriv
公共
$.hasData
$.cleanData
cloneCopyEvent
隊(duì)列
$().queue
$()._queueHooks
$().promise
動(dòng)畫
$().animate
$().stop
$().finish
showHide
事件
$.event.add
$.event.remove
$.event.dispatch
$.event.trigger
其它
setGlobalEval
domManip
defaultPrefilter
$().toggleClass
dataUser
公共
$.hasData
$.cleanData
cloneCopyEvent
數(shù)據(jù)緩存
$.data
$.removeData
$().data
$().removeData
其它
dataAttr
以上可以看到,除了“公共”,DataPriv 用在了 jQuery 的 隊(duì)列、動(dòng)畫、事件等模塊;dataUser 用在了數(shù)據(jù)緩存及dataAttr模塊。
“公共” 是指這三個(gè)方法內(nèi)都用到了 dataPriv 和 dataUser
$.hasData(elem)
用來判斷 elem 上是否綁定了相關(guān)的數(shù)據(jù)緩存,返回 true 和false,只有 dataPriv 和 dataUser 上都沒有才返回 false
源碼
hasData: function( elem ) {
return dataUser.hasData( elem ) || dataPriv.hasData( elem );
},
$.cleanData(elems)
清空 elem 上綁定的所有數(shù)據(jù)緩存,理所當(dāng)然的需要同時(shí)清空 dataPriv 和 dataUser 上的。
注意:雖然這個(gè)方法在公開暴露在了 $ 上, 但官網(wǎng)API上卻沒有該方法的介紹。另使用不當(dāng)會(huì)造成嚴(yán)重后果,比如綁定了事件后(.on),調(diào)用該方法,綁定的事件將全部失效。因?yàn)闀?huì)清空 dataPriv 內(nèi)的所有數(shù)據(jù)。
cloneCopyEvent(src, dest)
這是一個(gè)內(nèi)部方法,$.clone 會(huì)使用到它。克隆元素時(shí)除了會(huì)克隆node節(jié)點(diǎn)外,綁定在node上的數(shù)據(jù)也會(huì)被克隆過去。比如
var cloneNode = $.clone(elem);
把 elem 克隆給 cloneNode,此時(shí) elem 上添加的事件 cloneNode 上也會(huì)有。
三、1.x.x 和 2.x.x 的比較
jQuery 1.x 系列 和 2.x 系列的版本對(duì) 數(shù)據(jù)緩存模塊的實(shí)現(xiàn)差異還是很大的。大家可以對(duì)比我11年的這篇文章
1. 緩存的數(shù)據(jù)結(jié)構(gòu)
1.x (直到1.11.2) 緩存都是存儲(chǔ)在 jQuery.cache 上的,2.x(包括3.x) 則使用了一個(gè)內(nèi)部類 Data 做緩存,其主要用到了兩個(gè)對(duì)象 dataPriv 和 dataUser。很明顯 2.x 做的更好,它所有的緩存數(shù)據(jù)都是私有的,不會(huì)存在被誤寫的風(fēng)險(xiǎn),而 1.x 的 jQuery.cache 是公開的,如果被誤寫(比如某個(gè)同學(xué)想當(dāng)然的給$上添加一個(gè)cache對(duì)象)后果不堪設(shè)想。
2. jQuery._data
看到這個(gè)下劃線就知道是私有的(約定式),在 1.x 中是僅在內(nèi)部使用的,不提供給開發(fā)者。以 1.11.2 示例、這個(gè)方法被事件模塊、隊(duì)列模塊、動(dòng)畫模塊、setGlobalEval、cloneCopyEvent、fixCloneNodeIssues、domManip、showHide、defaultPrefilter、toggleClass 使用。3.x 則使用 dataPriv 和 dataUser 替代,大家可以對(duì)比看看。
(2/3).x 相比 1.x 明顯更優(yōu),dataPriv 和 dataUser 是真正的私有的(封裝的更好,更安全),比起 1.x 約定式的私有 jQuery._data。雖然 3.0.0 還保守的兼容了 jQuery._data,相信過不了多久的后續(xù)版本就會(huì)剔除。

3. 重構(gòu)
1.x 以 $._data 為中心,以它來輔助實(shí)現(xiàn)其它 API, (2/3).x 以 dataPriv/dataUser 為中心來實(shí)現(xiàn)。(2/3).x 將代碼重構(gòu)后提取出了 Data 類,更加清晰。
以上所述是小編給大家介紹的jQuery 3.0中的Data的全部敘述,希望對(duì)大家有所幫助,如果大家有任何疑問請給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
60個(gè)很實(shí)用的jQuery代碼開發(fā)技巧收集
這篇文章主要介紹了60個(gè)很實(shí)用的jQuery代碼開發(fā)技巧收集,使用jquery的朋友可以參考下核心代碼的使用,需要的朋友可以參考下2014-12-12
JQuery點(diǎn)擊事件回到頁面頂部效果的實(shí)現(xiàn)代碼
下面小編就為大家?guī)硪黄狫Query點(diǎn)擊事件回到頁面頂部效果的實(shí)現(xiàn)代碼。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-05-05
jquery實(shí)現(xiàn)多屏多圖焦點(diǎn)圖切換特效的方法
這篇文章主要介紹了jquery實(shí)現(xiàn)多屏多圖焦點(diǎn)圖切換特效的方法,涉及jQuery插件jquery.kinMaxShow實(shí)現(xiàn)焦點(diǎn)圖的相關(guān)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-05-05
用Jquery重寫windows.alert方法實(shí)現(xiàn)思路
本文將介紹下用Jquery重寫windows.alert方法,已經(jīng)在 IE8 , firefox3.0.11下面測試通過,喜歡的朋友可以放心使用2013-04-04
JQuery中DOM加載與事件執(zhí)行實(shí)例分析
這篇文章主要介紹了JQuery中DOM加載與事件執(zhí)行,實(shí)例分析了jQuery中DOM加載及事件執(zhí)行的原理與實(shí)現(xiàn)方法,并補(bǔ)充說明了windows.onload方法和$(document).ready()方法的區(qū)別,需要的朋友可以參考下2015-06-06

