深入學(xué)習(xí)jQuery中的data()
data有什么作用?
在我們平時(shí)js編碼過程中,我們經(jīng)常會(huì)向DOM元素中添加各種自定義屬性,這樣有一個(gè)弊端。
1、假設(shè)我們在DOM元素中添加了一個(gè)屬性,這個(gè)屬性指向了某個(gè)js對象。 dom1.ele = jsObj
2、當(dāng)這個(gè)js對象發(fā)揮完作用后,我們已經(jīng)用不到他了。這時(shí)候按理說應(yīng)該把這個(gè)js變量清空,釋放內(nèi)存。大家都知道,如果一個(gè)js對象不存在任何外在引用的話,解釋器會(huì)自動(dòng)將其在內(nèi)存中刪除,這也是javascript相對于c++等手動(dòng)管理內(nèi)存的程序的優(yōu)點(diǎn)。
3、但是這時(shí)候問題來了,因?yàn)镈OM元素引用了這個(gè)js對象,盡管這個(gè)js對象已經(jīng)沒有存在的意義了,但是解釋器是不會(huì)把他刪除的。如果想要把其刪除,我們可能需要將DOM元素的這個(gè)屬性設(shè)置為null。
4、我們編寫了這么多的代碼,哪里能把 每個(gè)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)建一個(gè)數(shù)據(jù)緩存池,這個(gè)緩存池專門用來存儲 向 DOM對象或者jQuery對象附加的額外數(shù)據(jù)。
2、當(dāng)我們要向DOM對象或者jQuery對象附加額外數(shù)據(jù)的時(shí)候,我們附加的數(shù)據(jù)其實(shí)是保存于這個(gè)緩存池中
3、DOM對象或者jQuery對象生成一個(gè)額外屬性,這個(gè)屬性保存了 附加數(shù)據(jù)在緩存池中的‘門牌號'(位置或者索引)
4、當(dāng)我們訪問DOM對象或者jQuery對象的附加數(shù)據(jù)時(shí),實(shí)際上是先取得其附加數(shù)據(jù)的門牌號,然后找到緩存池中對應(yīng)門牌號的數(shù)據(jù),進(jìn)行操作。
大體思路講完,那么來分析一下具體思路:
在jQuery中,有一個(gè)Data構(gòu)造函數(shù),每當(dāng)運(yùn)行這個(gè)構(gòu)造函數(shù)時(shí),就會(huì)生成一個(gè)實(shí)例。
jQuery默認(rèn)會(huì)自動(dòng)生成兩個(gè)Data實(shí)例:
var dataPriv = new Data()
jQuery私有的,我們盡量不要對這個(gè)實(shí)例進(jìn)行操作。
var dataUser = new Data()
這個(gè)就是服務(wù)于用戶了,我們使用data()方法都是對這個(gè)實(shí)例進(jìn)行操作。
所有的Data實(shí)例都有以下屬性:
expando: 值為字符串類型,每個(gè)Data實(shí)例的expando屬性的值都不相同,用來區(qū)分不同的Data實(shí)例,類似于id的作用,expando的值就是上文中的額外屬性。
uid: 這就是上文中的門牌號,初始為1,隨著不同對象的附加數(shù)據(jù)的加入,自增長。
cache : 一個(gè)對象 {} ,這就是緩存池了。
來個(gè)實(shí)例:
$(document.body).data('aaa', 'value-aaa') console.dir(document.body)
body對象有一個(gè)名為jquer210023......的額外屬性,
這個(gè)屬性的名稱就是dataUser的expando的值
這個(gè)屬性的值就是門牌號。
總結(jié): data實(shí)際上就是對js對象或者DOM對象的額外屬性做了一個(gè)集中的管理。對于那些不會(huì)產(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]] accessor Object.defineProperty( this.cache = {}, 0, { get: function() { return {}; } }); // jQuery.expando = "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ) expando是一個(gè)jQuery的唯一標(biāo)示 // 格式是:'jQuery\\d*' 也就是'jQuery'+ 多個(gè)數(shù)字。這里為啥要搞得這么麻煩呢? // 應(yīng)因?yàn)槲覀兛赡軙?huì)創(chuàng)建多個(gè)Data對象,為了保證每個(gè)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(是一個(gè)數(shù)字), // 若owner在該緩存池中不存在對應(yīng)的緩存對象,則在緩存池中為其創(chuàng)建一個(gè)緩存對象,并返回該緩存對象的key if ( !Data.accepts( owner ) ) { return 0; } var descriptor = {}, // Check if the owner object already has a cache key // 檢查owner對象在該緩存池中是否存在緩存 unlock = owner[ this.expando ]; // 是一個(gè)數(shù)字,用來作為緩存池中緩存對象的key // If not, create one // 如果沒有,則創(chuàng)建一個(gè) if ( !unlock ) { unlock = Data.uid++; // Secure it in a non-enumerable, non-writable property // 給owner附加一個(gè)屬性 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 ] = {}; } // 返回unlock return 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 existed unlock = this.key( owner ), // 獲取owner的對應(yīng)的緩存對象在緩存池中的key(這里的key,是鍵值對中的鍵的意思) cache = this.cache[ unlock ]; // 獲取owner所對應(yīng)的緩存對象 // Handle: [ owner, key, value ] args // 根據(jù)傳入?yún)?shù)的個(gè)數(shù)以及類型實(shí)現(xiàn)重載 if ( typeof data === "string" ) { cache[ data ] = value; // 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 ]; } } } // 返回緩存對象 return cache; }, get: function( owner, key ) { // 獲取owner對象的名為key的屬性值 // owner:是一個(gè)對象(可以是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的話就返回整個(gè)緩存對象,若指定了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 整個(gè)緩存對象 // 2. The data stored at the key 緩存對象中某個(gè)鍵的值 // if ( key === undefined || // 沒有指定key或者指定了字符串格式的key,但沒有指定value ((key && typeof key === "string") && value === undefined) ) { // 沒有指定key:獲取整個(gè)緩存對象 // 指定了字符串格式的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不是一個(gè)字符串,或者key和value都指定了,就會(huì)根據(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)的緩存對象,或者移除緩存對象中的某個(gè)鍵值對 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 keys if ( 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 manipulation if ( key in cache ) { name = [ key, camel ]; } else { // If a key with the spaces exists, use it. // Otherwise, create an array by matching non-whitespace name = 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; });
可能會(huì)有同學(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)的不同。
通過上面的源碼,我們可以看到jQuery.data(ele,name,data)
是對ele元素附加數(shù)據(jù)。
而jQuery().data(key,value)
則會(huì)為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對象中的每個(gè)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
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者動(dòng)作能帶來一定的幫助,如果有疑問大家可以留言交流。
相關(guān)文章
基于JQuery的模擬蘋果桌面Dock效果(穩(wěn)定版)
之所以將它命名為穩(wěn)定版,是因?yàn)橹耙呀?jīng)分享了一個(gè)初級版本的,之前的初級版中存在很多bug。現(xiàn)在經(jīng)過反復(fù)琢磨、實(shí)驗(yàn),修復(fù)了之前版本存在的很多不足之處,就算鼠標(biāo)快速的滑動(dòng)依然表現(xiàn)的很穩(wěn)定2012-10-10jquery 設(shè)置style:display的方法
這篇文章主要介紹了jquery 設(shè)置style:display的方法,需要的朋友可以參考下2015-01-01jquery實(shí)現(xiàn)帶二級菜單的導(dǎo)航示例
這篇文章主要介紹了jquery實(shí)現(xiàn)帶二級菜單的導(dǎo)航示例,需要的朋友可以參考下2014-04-04jQuery實(shí)現(xiàn)點(diǎn)擊按鈕彈出可拖拽模態(tài)對話框完整實(shí)例【測試可用】
這篇文章主要介紹了jQuery實(shí)現(xiàn)點(diǎn)擊按鈕彈出可拖拽模態(tài)對話框的方法,結(jié)合完整實(shí)例形式分析了jQuery調(diào)用模態(tài)對話框的基本原理、實(shí)現(xiàn)方法與相關(guān)操作技巧,需要的朋友可以參考下2023-04-04Jquery實(shí)現(xiàn)上下移動(dòng)和排序代碼
這篇文章主要介紹了Jquery實(shí)現(xiàn)上下移動(dòng)和排序,想要學(xué)習(xí)Jquery的同學(xué)可以來了解一下。具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-10-10基于jQuery實(shí)現(xiàn)文本框只能輸入數(shù)字(小數(shù)、整數(shù))
在實(shí)際應(yīng)用中,文本框中有時(shí)候只能夠允許輸入整數(shù),但是有時(shí)候可能更為"博愛"一點(diǎn),可以允許輸入浮點(diǎn)數(shù),下面就通過實(shí)例代碼介紹一下如何利用jquery實(shí)現(xiàn)此功能2016-01-01jQuery幻燈片特效代碼分享 鼠標(biāo)滑過按鈕時(shí)切換(2)
本文實(shí)例講述了jQuery實(shí)現(xiàn)時(shí)尚漂亮的幻燈片特效,基本能滿足你在網(wǎng)頁上使用幻燈片(焦點(diǎn)圖)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2015-08-08