jQuery 3.0 在6月9日正式發佈了,3.0 也被稱爲下一代的 jQuery 。這個版本從14年10月開始,其中發佈過一次beta 版(2016/1/14,)和候選版(2016/05/20)。一路走來,頗爲不易。javascript
文章目錄html
jQuery 3.0 中的 Data 是內部使用的,定義爲一個「類」。一共用它建立了兩個對象,dataPriv 和 dataUser。Data 有 1 個對象屬性(expando)和類屬性(uid),有 6 個方法,以下html5
下面分別解讀java
這是一個從 1 開始用來自增的數字。node
由 jQuery.expando 和 uid 組合而成,它用來做爲元素(如DOM元素)的key,是惟一的。jQuery.expando 的生成以下jquery
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 以下promise
jQuery 300 024727210109188635 1 jQuery 300 024727210109188635 2
第三部分是隨機數,每次刷新都會變,其它部分的不變。緩存
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 處使用,分別爲
若是是 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; },
上面的 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; },
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 ) ]; },
這個方法即時 getter,也是 setter,如此而已。它存在的緣由是因爲 jQuery 的 API 有不少是身兼 setter 和 getter 的功能,好比 .data、.html、.text。有了 access 能夠少寫不少 if / else。
getter 條件
setter 條件
源碼
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; },
清空綁定元素(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 ]; } } },
用來判斷 owner 上是否有緩存數據,返回 true 或 false。
源碼
hasData: function( owner ) { var cache = owner[ this.expando ]; return cache !== undefined && !jQuery.isEmptyObject( cache ); }
以上解讀完了 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 上也會有。
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