深入學習jQuery中的data()
data有什么作用?
在我們平時js編碼過程中,我們經(jīng)常會向DOM元素中添加各種自定義屬性,這樣有一個弊端。
1、假設我們在DOM元素中添加了一個屬性,這個屬性指向了某個js對象。 dom1.ele = jsObj
2、當這個js對象發(fā)揮完作用后,我們已經(jīng)用不到他了。這時候按理說應該把這個js變量清空,釋放內(nèi)存。大家都知道,如果一個js對象不存在任何外在引用的話,解釋器會自動將其在內(nèi)存中刪除,這也是javascript相對于c++等手動管理內(nèi)存的程序的優(yōu)點。
3、但是這時候問題來了,因為DOM元素引用了這個js對象,盡管這個js對象已經(jīng)沒有存在的意義了,但是解釋器是不會把他刪除的。如果想要把其刪除,我們可能需要將DOM元素的這個屬性設置為null。
4、我們編寫了這么多的代碼,哪里能把 每個js對象是不是被DOM元素引用了都記住啊?
5、而且,假如DOM元素與js對象之間相互循環(huán)引用,根本就無法刪除! 這就是內(nèi)存泄漏
6、所以,為了避免這種情況的發(fā)生,我們要盡量避免 引用數(shù)據(jù)(這里的引用數(shù)據(jù)可以說是javascript對象) 直接依附在DOM對象上。
7、data就是用來搞定以上問題的方法。
data是如何搞定以上問題的?
首先來說一說jQuery中Data實現(xiàn)的大體思路:
1、首先我們創(chuàng)建一個數(shù)據(jù)緩存池,這個緩存池專門用來存儲 向 DOM對象或者jQuery對象附加的額外數(shù)據(jù)。
2、當我們要向DOM對象或者jQuery對象附加額外數(shù)據(jù)的時候,我們附加的數(shù)據(jù)其實是保存于這個緩存池中
3、DOM對象或者jQuery對象生成一個額外屬性,這個屬性保存了 附加數(shù)據(jù)在緩存池中的‘門牌號'(位置或者索引)
4、當我們訪問DOM對象或者jQuery對象的附加數(shù)據(jù)時,實際上是先取得其附加數(shù)據(jù)的門牌號,然后找到緩存池中對應門牌號的數(shù)據(jù),進行操作。
大體思路講完,那么來分析一下具體思路:
在jQuery中,有一個Data構造函數(shù),每當運行這個構造函數(shù)時,就會生成一個實例。
jQuery默認會自動生成兩個Data實例:
var dataPriv = new Data() jQuery私有的,我們盡量不要對這個實例進行操作。
var dataUser = new Data() 這個就是服務于用戶了,我們使用data()方法都是對這個實例進行操作。
所有的Data實例都有以下屬性:
expando: 值為字符串類型,每個Data實例的expando屬性的值都不相同,用來區(qū)分不同的Data實例,類似于id的作用,expando的值就是上文中的額外屬性。
uid: 這就是上文中的門牌號,初始為1,隨著不同對象的附加數(shù)據(jù)的加入,自增長。
cache : 一個對象 {} ,這就是緩存池了。
來個實例:
$(document.body).data('aaa', 'value-aaa')
console.dir(document.body)
body對象有一個名為jquer210023......的額外屬性,
這個屬性的名稱就是dataUser的expando的值
這個屬性的值就是門牌號。
總結: data實際上就是對js對象或者DOM對象的額外屬性做了一個集中的管理。對于那些不會產(chǎn)生內(nèi)存泄漏的額外數(shù)據(jù),我們也可以直接向js對象或者DOM對象附加。
好,理清楚上面的關系后,我們再來看一下源碼:
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是一個jQuery的唯一標示
// 格式是:'jQuery\\d*' 也就是'jQuery'+ 多個數(shù)字。這里為啥要搞得這么麻煩呢?
// 應因為我們可能會創(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在該緩存池中存在對應的緩存對象,則返回混存對象的key(是一個數(shù)字),
// 若owner在該緩存池中不存在對應的緩存對象,則在緩存池中為其創(chuàng)建一個緩存對象,并返回該緩存對象的key
if ( !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對應的緩存對象已存在
if ( !this.cache[ unlock ] ) {
this.cache[ unlock ] = {};
}
// 返回unlock
return unlock;
},
set: function( owner, data, value ) {
// 設置owner對應的緩存對象
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的對應的緩存對象在緩存池中的key(這里的key,是鍵值對中的鍵的意思)
cache = this.cache[ unlock ]; // 獲取owner所對應的緩存對象
// Handle: [ owner, key, value ] args
// 根據(jù)傳入?yún)?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:是一個對象(可以是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:
// 當key不是一個字符串,或者key和value都指定了,就會根據(jù)情況進行設置或者擴展
//
// 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對應的緩存對象,或者移除緩存對象中的某個鍵值對
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;
});
可能會有同學問道:如果我想對dataPriv進行操作該如何?
請看源碼:
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進行操作,而jQuery._data() jQuery._remove()都是對dataPriv進行操作。
理解jQuery.data(ele,name,data) 與 jQuery().data(key,value)的不同。
通過上面的源碼,我們可以看到jQuery.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
總結
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者動作能帶來一定的幫助,如果有疑問大家可以留言交流。
相關文章
基于JQuery的模擬蘋果桌面Dock效果(穩(wěn)定版)
之所以將它命名為穩(wěn)定版,是因為之前已經(jīng)分享了一個初級版本的,之前的初級版中存在很多bug?,F(xiàn)在經(jīng)過反復琢磨、實驗,修復了之前版本存在的很多不足之處,就算鼠標快速的滑動依然表現(xiàn)的很穩(wěn)定2012-10-10
jQuery實現(xiàn)點擊按鈕彈出可拖拽模態(tài)對話框完整實例【測試可用】
這篇文章主要介紹了jQuery實現(xiàn)點擊按鈕彈出可拖拽模態(tài)對話框的方法,結合完整實例形式分析了jQuery調(diào)用模態(tài)對話框的基本原理、實現(xiàn)方法與相關操作技巧,需要的朋友可以參考下2023-04-04
基于jQuery實現(xiàn)文本框只能輸入數(shù)字(小數(shù)、整數(shù))
在實際應用中,文本框中有時候只能夠允許輸入整數(shù),但是有時候可能更為"博愛"一點,可以允許輸入浮點數(shù),下面就通過實例代碼介紹一下如何利用jquery實現(xiàn)此功能2016-01-01

