深入jQuery中的data()
引入
data函數(shù)在jQuery中看起來很不起眼, 就像沙灘上一顆平凡的沙子, 但仔細(xì)一瞅, 卻驚訝的發(fā)現(xiàn)data是jQuery中無比重要的一環(huán), 甚至jQuery中各種事件都基于此。
data有什么作用?
在我們平時js編碼過程中,我們經(jīng)常會向DOM元素中添加各種自定義屬性,這樣有一個弊端。
1 ?假設(shè)我們在DOM元素中添加了一個屬性,這個屬性指向了某個js對象。 dom1.ele = jsObj
2 ?當(dāng)這個js對象發(fā)揮完作用后,我們已經(jīng)用不到他了。這時候按理說應(yīng)該把這個js變量清空,釋放內(nèi)存。大家都知道,如果一個js對象不存在任何外在引用的話,解釋器會自動將其在內(nèi)存中刪除,這也是javascript相對于c++等手動管理內(nèi)存的程序的優(yōu)點(diǎn)。
3 ?但是這時候問題來了,因?yàn)镈OM元素引用了這個js對象,盡管這個js對象已經(jīng)沒有存在的意義了,但是解釋器是不會把他刪除的。如果想要把其刪除,我們可能需要將DOM元素的這個屬性設(shè)置為null。
4 ?我們編寫了這么多的代碼,哪里能把 每個js對象是不是被DOM元素引用了都記住啊?
5 ?而且,假如DOM元素與js對象之間相互循環(huán)引用,根本就無法刪除! 這就是內(nèi)存泄漏
6 ?所以,為了避免這種情況的發(fā)生,我們要盡量避免 引用數(shù)據(jù)(這里的引用數(shù)據(jù)可以說是javascript對象) 直接依附在DOM對象上。
7 ?data就是用來搞定以上問題的方法。
data是如何搞定以上問題的?
首先來說一說jQuery中Data實(shí)現(xiàn)的大體思路:
1 ?首先我們創(chuàng)建一個數(shù)據(jù)緩存池,這個緩存池專門用來存儲 ?向 DOM對象或者jQuery對象附加的額外數(shù)據(jù)。
2 ?當(dāng)我們要向DOM對象或者jQuery對象附加額外數(shù)據(jù)的時候,我們附加的數(shù)據(jù)其實(shí)是保存于這個緩存池中
3 ?DOM對象或者jQuery對象生成一個額外屬性,這個屬性保存了 附加數(shù)據(jù)在緩存池中的‘門牌號’(位置或者索引)
4 ?當(dāng)我們訪問DOM對象或者jQuery對象的附加數(shù)據(jù)時,實(shí)際上是先取得其附加數(shù)據(jù)的門牌號,然后找到緩存池中對應(yīng)門牌號的數(shù)據(jù),進(jìn)行操作。
大體思路講完,那么來分析一下具體思路:
在jQuery中,有一個Data構(gòu)造函數(shù),每當(dāng)運(yùn)行這個構(gòu)造函數(shù)時,就會生成一個實(shí)例。jQuery默認(rèn)會自動生成兩個Data實(shí)例:
var dataPriv = new Data() ? jQuery私有的,我們盡量不要對這個實(shí)例進(jìn)行操作。
var dataUser = new Data() ? 這個就是服務(wù)于用戶了,我們使用data()方法都是對這個實(shí)例進(jìn)行操作。
所有的Data實(shí)例都有以下屬性:
expando: ?值為字符串類型,每個Data實(shí)例的expando屬性的值都不相同,用來區(qū)分不同的Data實(shí)例,類似于id的作用,expando的值就是上文中的額外屬性。
uid: ? 這就是上文中的門牌號,初始為1,隨著不同對象的附加數(shù)據(jù)的加入,自增長。
cache : 一個對象 {} ,這就是緩存池了。
來個實(shí)例:
$(document.body).data('aaa', 'value-aaa')
console.dir(document.body) body對象有一個名為jquer210023......的額外屬性,
這個屬性的名稱就是dataUser的expando的值
這個屬性的值就是門牌號。
?
總結(jié): data實(shí)際上就是對js對象或者DOM對象的額外屬性做了一個集中的管理。對于那些不會產(chǎn)生內(nèi)存泄漏的額外數(shù)據(jù),我們也可以直接向js對象或者DOM對象附加。
?
好,理清楚上面的關(guān)系后,我們再來看一下源碼:
define(["../core","../var/rnotwhite","./accepts" ], function( jQuery, rnotwhite ) {function Data() {// Support: Android<4,// Old WebKit does not have Object.preventExtensions/freeze method,// return new empty object instead with no [[set]] accessorObject.defineProperty( this.cache = {}, 0, {get: function() {return {};}});// jQuery.expando = "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ) expando是一個jQuery的唯一標(biāo)示// 格式是:'jQuery\\d*' 也就是'jQuery'+ 多個數(shù)字。這里為啥要搞得這么麻煩呢?// 應(yīng)因?yàn)槲覀兛赡軙?chuàng)建多個Data對象,為了保證每個Data對象的expando屬性的值不相等,所以這么搞this.expando = jQuery.expando + Math.random(); }Data.uid = 1; // Data函數(shù)的屬性,'靜態(tài)屬性' Data.accepts = jQuery.acceptData;Data.prototype = {key: function( owner ) {// We can accept data for non-element nodes in modern browsers,// but we should not, see #8335.// Always return the key for a frozen object.// 若owner在該緩存池中存在對應(yīng)的緩存對象,則返回混存對象的key(是一個數(shù)字),// 若owner在該緩存池中不存在對應(yīng)的緩存對象,則在緩存池中為其創(chuàng)建一個緩存對象,并返回該緩存對象的keyif ( !Data.accepts( owner ) ) {return 0;}var descriptor = {},// Check if the owner object already has a cache key// 檢查owner對象在該緩存池中是否存在緩存unlock = owner[ this.expando ]; // 是一個數(shù)字,用來作為緩存池中緩存對象的key// If not, create one// 如果沒有,則創(chuàng)建一個if ( !unlock ) {unlock = Data.uid++;// Secure it in a non-enumerable, non-writable property// 給owner附加一個屬性 owner[this.expando] = unlock ,并且該屬性不能被枚舉,try {descriptor[ this.expando ] = { value: unlock };Object.defineProperties( owner, descriptor );// Support: Android<4// Fallback to a less secure definition} catch ( e ) {descriptor[ this.expando ] = unlock;jQuery.extend( owner, descriptor );}}// Ensure the cache object// 確保owner對應(yīng)的緩存對象已存在if ( !this.cache[ unlock ] ) {this.cache[ unlock ] = {};}// 返回unlockreturn unlock;},set: function( owner, data, value ) {// 設(shè)置owner對應(yīng)的緩存對象var prop,// There may be an unlock assigned to this node,// if there is no entry for this "owner", create one inline// and set the unlock as though an owner entry had always existedunlock = this.key( owner ), // 獲取owner的對應(yīng)的緩存對象在緩存池中的key(這里的key,是鍵值對中的鍵的意思)cache = this.cache[ unlock ]; // 獲取owner所對應(yīng)的緩存對象// Handle: [ owner, key, value ] args// 根據(jù)傳入?yún)?shù)的個數(shù)以及類型實(shí)現(xiàn)重載if ( typeof data === "string" ) {cache[ data ] = value;// Handle: [ owner, { properties } ] args} else {// Fresh assignments by object are shallow copiedif ( 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 ];}}}// 返回緩存對象return cache;},get: function( owner, key ) {// 獲取owner對象的名為key的屬性值// owner:是一個對象(可以是jQuery對象也可以是DOM對象) key: 屬性名// Either a valid cache is found, or will be created.// New caches will be created and the unlock returned,// allowing direct access to the newly created// empty data object. A valid owner object must be provided.var cache = this.cache[ this.key( owner ) ]; // owner的緩存對象return key === undefined ? cache : cache[ key ]; // 沒指定key的話就返回整個緩存對象,若指定了key則返回在該緩存對象的key屬性的值 },access: function( owner, key, value ) {var stored;// In cases where either:// // 1. No key was specified 沒有指定key// 2. A string key was specified, but no value provided 指定了字符串格式的key,但沒有指定value// // 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或者指定了字符串格式的key,但沒有指定value((key && typeof key === "string") && value === undefined) ) {// 沒有指定key:獲取整個緩存對象// 指定了字符串格式的key,但沒有指定value: 獲取緩存對象中key的值stored = this.get( owner, key );return stored !== undefined ?stored : this.get( owner, jQuery.camelCase(key) );}// [*]When the key is not a string, or both a key and value// are specified, set or extend (existing objects) with either:// 當(dāng)key不是一個字符串,或者key和value都指定了,就會根據(jù)情況進(jìn)行設(shè)置或者擴(kuò)展// // 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;},remove: function( owner, key ) {// 清空owner對應(yīng)的緩存對象,或者移除緩存對象中的某個鍵值對var i, name, camel,unlock = this.key( owner ),cache = this.cache[ unlock ];// 如果沒有指定key,則清空緩存對象if ( key === undefined ) {this.cache[ unlock ] = {};} else {// Support array or space separated string of keysif ( jQuery.isArray( key ) ) {// If "name" is an array of keys...// When data is initially created, via ("key", "val") signature,// keys will be converted to camelCase.// Since there is no way to tell _how_ a key was added, remove// both plain key and camelCase key. #12786// This will only penalize the array argument path.name = key.concat( key.map( jQuery.camelCase ) );} else {camel = jQuery.camelCase( key );// Try the string as a key before any manipulationif ( key in cache ) {name = [ key, camel ];} else {// If a key with the spaces exists, use it.// Otherwise, create an array by matching non-whitespacename = camel;name = name in cache ?[ name ] : ( name.match( rnotwhite ) || [] );}}i = name.length;while ( i-- ) {delete cache[ name[ i ] ];}}},hasData: function( owner ) {// 檢查owner在該緩存池中是否存在緩存對象return !jQuery.isEmptyObject(this.cache[ owner[ this.expando ] ] || {});},discard: function( owner ) {if ( owner[ this.expando ] ) {delete this.cache[ owner[ this.expando ] ];}} };return Data; });Data構(gòu)造函數(shù)源碼解析
?
可能會有同學(xué)問道:如果我想對dataPriv進(jìn)行操作該如何?
請看源碼:
jQuery.extend({hasData: function( elem ) {return dataUser.hasData( elem ) || dataPriv.hasData( elem );},data: function( elem, name, data ) {return dataUser.access( elem, name, data );},removeData: function( elem, name ) {dataUser.remove( elem, name );},// TODO: Now that all calls to _data and _removeData have been replaced// with direct calls to dataPriv methods, these can be deprecated._data: function( elem, name, data ) {return dataPriv.access( elem, name, data );},_removeData: function( elem, name ) {dataPriv.remove( elem, name );} });
通過源碼,我們可以看出:
jQuery.data() jQuery.remove()都是對dataUser進(jìn)行操作,而jQuery._data() jQuery._remove()都是對dataPriv進(jìn)行操作。
?
理解jQuery.data(ele,name,data) 與 jQuery().data(key,value)的不同。
通過上面的源碼,我們可以看到j(luò)Query.data(ele,name,data)是對ele元素附加數(shù)據(jù)。
而jQuery().data(key,value)則會為jQuery對象中的所有DOM對象分別附加數(shù)據(jù)
來看源碼(刪減了部分):
jQuery.fn.extend({data: function( key, value ) {var i, name, data,elem = this[ 0 ],attrs = elem && elem.attributes;return access( this, function( value ) {var data,camelKey = jQuery.camelCase( key );
// 從這里可以看出,為jQuery對象中的每個DOM元素分別附加數(shù)據(jù)this.each(function() {// First, attempt to store a copy or reference of any// data that might've been store with a camelCased key.var data = dataUser.get( this, camelKey );// For HTML5 data-* attribute interop, we have to// store property names with dashes in a camelCase form.// This might not apply to all properties...*dataUser.set( this, camelKey, value );// *... In the case of properties that might _actually_// have dashes, we need to also store a copy of that// unchanged property.if ( key.indexOf("-") !== -1 && data !== undefined ) {dataUser.set( this, key, value );}});}, null, value, arguments.length > 1, null, true );},removeData: function( key ) {return this.each(function() {dataUser.remove( this, key );});} });
?-----------------------------------------------分隔線---------------------------------------------------
上文中的所有源碼:為jQuery.1.12??
轉(zhuǎn)載于:https://www.cnblogs.com/MnCu8261/p/6105103.html
總結(jié)
以上是生活随笔為你收集整理的深入jQuery中的data()的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 多少钱纳税啊?
- 下一篇: 《重到城七绝句·张十八》第一句是什么