jQuery中數(shù)據(jù)緩存$.data的用法及源碼完全解析
一、實現(xiàn)原理:
對于DOM元素,通過分配一個唯一的關(guān)聯(lián)id把DOM元素和該DOM元素的數(shù)據(jù)緩存對象關(guān)聯(lián)起來,關(guān)聯(lián)id被附加到以jQuery.expando的值命名的屬性上,數(shù)據(jù)存儲在全局緩存對象jQuery.cache中。在讀取、設(shè)置、移除數(shù)據(jù)時,將通過關(guān)聯(lián)id從全局緩存對象jQuery.cache中找到關(guān)聯(lián)的數(shù)據(jù)緩存對象,然后在數(shù)據(jù)緩存對象上執(zhí)行讀取、設(shè)置、移除操作。
對于Javascript對象,數(shù)據(jù)則直接存儲在該Javascript對象的屬性jQuery.expando上。在讀取、設(shè)置、移除數(shù)據(jù)時,實際上是對Javascript對象的數(shù)據(jù)緩存對象執(zhí)行讀取、設(shè)置、移除操作。
為了避免jQuery內(nèi)部使用的數(shù)據(jù)和用戶自定義的數(shù)據(jù)發(fā)生沖突,數(shù)據(jù)緩存模塊把內(nèi)部數(shù)據(jù)存儲在數(shù)據(jù)緩存對象上,把自定義數(shù)據(jù)存儲在數(shù)據(jù)緩存對象的屬性data上。
二、總體結(jié)構(gòu):
// 數(shù)據(jù)緩存 Data jQuery.extend({ // 全局緩存對象 cache: {}, // 唯一 id種子 uuid:0, // 頁面中每個jQuery副本的唯一標識 expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), // 是否有關(guān)聯(lián)的數(shù)據(jù) hasData: function(){}, // 設(shè)置、讀取自定數(shù)據(jù)或內(nèi)部數(shù)據(jù) data: function(elem, name, data, pvt) {}, // 移除自定義數(shù)據(jù)或內(nèi)部數(shù)據(jù) removeData: function(elem, name, pvt) {}, // 設(shè)置、讀取內(nèi)部數(shù)據(jù) _data: function(elem, name, data) {}, // 是否可以設(shè)置數(shù)據(jù) acceptData: function(elem){} }); jQuery.fn.extend({ // 設(shè)置、讀取自定義數(shù)據(jù),解析HTML5屬性data- data: function(key,value){}, // 移除自定義數(shù)據(jù) removeData: function(key){} }); // 解析HTML5屬性 data- function dataAttr(elem,key,data){} // 檢查數(shù)據(jù)緩存對象是否為空 function isEmptyDataObject(obj){} jQuery.extend({ // 清空數(shù)據(jù)緩存對象 cleanData: function(elems){} });
三、$.data(elem, name, data), $.data(elem, name)
$.data(elem, name, data)的使用方法:
如果傳入?yún)?shù)name, data, 則設(shè)置任意類型的數(shù)據(jù)
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>jQuery.data demo</title> <style> div { color: blue; } span { color: red; } </style> <script src="http://code.jquery.com/jquery-1.10.2.js"></script> </head> <body> <div> The values stored were <span></span> and <span></span> </div> <script> var div = $( "div" )[ 0 ]; jQuery.data( div, "test", { first: 16, last: "pizza!" }); $( "span:first" ).text( jQuery.data( div, "test" ).first ); $( "span:last" ).text( jQuery.data( div, "test" ).last ); </script> </body> </html>
$.data(elem, name)的使用方法:
如果傳入key, 未傳入?yún)?shù)data, 則讀取并返回指定名稱的數(shù)據(jù)
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>jQuery.data demo</title> <style> div { margin: 5px; background: yellow; } button { margin: 5px; font-size: 14px; } p { margin: 5px; color: blue; } span { color: red; } </style> <script src="http://code.jquery.com/jquery-1.10.2.js"></script> </head> <body> <div>A div</div> <button>Get "blah" from the div</button> <button>Set "blah" to "hello"</button> <button>Set "blah" to 86</button> <button>Remove "blah" from the div</button> <p>The "blah" value of this div is <span>?</span></p> <script> $( "button" ).click( function() { var value, div = $( "div" )[ 0 ]; switch ( $( "button" ).index( this ) ) { case 0 : value = jQuery.data( div, "blah" ); break; case 1 : jQuery.data( div, "blah", "hello" ); value = "Stored!"; break; case 2 : jQuery.data( div, "blah", 86 ); value = "Stored!"; break; case 3 : jQuery.removeData( div, "blah" ); value = "Removed!"; break; } $( "span" ).text( "" + value ); }); </script> </body> </html>
$.data(elem, name, data), $.data(elem, name) 源碼解析:
jQuery.extend({ // 1. 定義jQuery.data(elem, name, data, pvt) data: function( elem, name, data, pvt /* Internal Use Only */ ) { // 2. 檢查是否可以設(shè)置數(shù)據(jù) if ( !jQuery.acceptData( elem ) ) { return; // 如果參數(shù)elem不支持設(shè)置數(shù)據(jù),則立即返回 } // 3 定義局部變量 var privateCache, thisCache, ret, internalKey = jQuery.expando, getByName = typeof name === "string", // We have to handle DOM nodes and JS objects differently because IE6-7 // can't GC object references properly across the DOM-JS boundary isNode = elem.nodeType, // elem是否是DOM元素 // Only DOM nodes need the global jQuery cache; JS object data is // attached directly to the object so GC can occur automatically cache = isNode ? jQuery.cache : elem, // 如果是DOM元素,為了避免javascript和DOM元素之間循環(huán)引用導(dǎo)致的瀏覽器(IE6/7)垃圾回收機制不起作用,要把數(shù)據(jù)存儲在全局緩存對象jQuery.cache中;對于javascript對象,來及回收機制能夠自動發(fā)生,不會有內(nèi)存泄露的問題,因此數(shù)據(jù)可以查收存儲在javascript對象上 // Only defining an ID for JS objects if its cache already exists allows // the code to shortcut on the same path as a DOM node with no cache id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey, isEvents = name === "events"; // Avoid doing any more work than we need to when trying to get data on an // object that has no data at all // 4. 如果是讀取數(shù)據(jù),但沒有數(shù)據(jù),則返回 if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { return; // getByName && data === undefined 如果name是字符串,data是undefined, 說明是在讀取數(shù)據(jù) // !id || !cache[id] || (!isEvents && !pvt && !cache[id].data 如果關(guān)聯(lián)id不存在,說明沒有數(shù)據(jù);如果cache[id]不存在,也說明沒有數(shù)據(jù);如果是讀取自動以數(shù)據(jù),但cache[id].data不存在,說明沒有自定義數(shù)據(jù) } // 5. 如果關(guān)聯(lián)id不存在,則分配一個 if ( !id ) { // Only DOM nodes need a new unique ID for each element since their data // ends up in the global cache if ( isNode ) { elem[ internalKey ] = id = ++jQuery.uuid; // 對于DOM元素,jQuery.uuid會自動加1,并附加到DOM元素上 } else { id = internalKey; // 對于javascript對象,關(guān)聯(lián)id就是jQuery.expando } } // 6. 如果數(shù)據(jù)緩存對象不存在,則初始化為空對象{} if ( !cache[ id ] ) { cache[ id ] = {}; // Avoids exposing jQuery metadata on plain JS objects when the object // is serialized using JSON.stringify if ( !isNode ) { cache[ id ].toJSON = jQuery.noop; // 對于javascript對象,設(shè)置方法toJSON為空函數(shù),以避免在執(zhí)行JSON.stringify()時暴露緩存數(shù)據(jù)。如果一個對象定義了方法toJSON(),JSON.stringify()在序列化該對象時會調(diào)用這個方法來生成該對象的JSON元素 } } // An object can be passed to jQuery.data instead of a key/value pair; this gets // shallow copied over onto the existing cache // 7. 如果參數(shù)name是對象或函數(shù),則批量設(shè)置數(shù)據(jù) if ( typeof name === "object" || typeof name === "function" ) { if ( pvt ) { cache[ id ] = jQuery.extend( cache[ id ], name ); // 對于內(nèi)部數(shù)據(jù),把參數(shù)name中的屬性合并到cache[id]中 } else { cache[ id ].data = jQuery.extend( cache[ id ].data, name ); // 對于自定義數(shù)據(jù),把參數(shù)name中的屬性合并到cache[id].data中 } } // 8. 如果參數(shù)data不是undefined, 則設(shè)置單個數(shù)據(jù) privateCache = thisCache = cache[ id ]; // jQuery data() is stored in a separate object inside the object's internal data // cache in order to avoid key collisions between internal data and user-defined // data. if ( !pvt ) { if ( !thisCache.data ) { thisCache.data = {}; } thisCache = thisCache.data; } if ( data !== undefined ) { thisCache[ jQuery.camelCase( name ) ] = data; } // Users should not attempt to inspect the internal events object using jQuery.data, // it is undocumented and subject to change. But does anyone listen? No. // 9. 特殊處理events if ( isEvents && !thisCache[ name ] ) { // 如果參數(shù)name是字符串"events",并且未設(shè)置過自定義數(shù)據(jù)"events",則返回事件婚車對象,在其中存儲了事件監(jiān)聽函數(shù)。 return privateCache.events; } // Check for both converted-to-camel and non-converted data property names // If a data property was specified //10. 如果參數(shù)name是字符串,則讀取單個數(shù)據(jù) if ( getByName ) { // First Try to find as-is property data ret = thisCache[ name ]; // 先嘗試讀取參數(shù)name對應(yīng)的數(shù)據(jù) // Test for null|undefined property data if ( ret == null ) { // 如果未取到,則把參數(shù)name轉(zhuǎn)換為駝峰式再次嘗試讀取對應(yīng)的數(shù)據(jù) // Try to find the camelCased property ret = thisCache[ jQuery.camelCase( name ) ]; } } else { // 11. 如果未傳入?yún)?shù)name,data,則返回數(shù)據(jù)緩存對象 ret = thisCache; } return ret; }, // For internal use only. _data: function( elem, name, data ) { return jQuery.data( elem, name, data, true ); }, });
四、.data(key, value), .data(key)
使用方法:
$( "body" ).data( "foo", 52 ); // 傳入key, value $( "body" ).data( "bar", { myType: "test", count: 40 } ); // 傳入key, value $( "body" ).data( { baz: [ 1, 2, 3 ] } ); // 傳入key, value $( "body" ).data( "foo" ); // 52 // 傳入key $( "body" ).data(); // 未傳入?yún)?shù)
HTML5 data attriubutes:
<div data-role="page" data-last-value="43" data-hidden="true" data-options='{"name":"John"}'></div> $( "div" ).data( "role" ) === "page"; $( "div" ).data( "lastValue" ) === 43; $( "div" ).data( "hidden" ) === true; $( "div" ).data( "options" ).name === "John";
.data(key, value), .data(key) 源碼解析
jQuery.fn.extend({ // 1. 定義.data(key, value) data: function( key, value ) { var parts, attr, name, data = null; // 2. 未傳入?yún)?shù)的情況 if ( typeof key === "undefined" ) { if ( this.length ) { // 如果參數(shù)key是undefined, 即參數(shù)格式是.data(), 則調(diào)用方法jQuery.data(elem, name, data, pvt)獲取第一個匹配元素關(guān)聯(lián)的自定義數(shù)據(jù)緩存對象,并返回。 data = jQuery.data( this[0] ); if ( this[0].nodeType === 1 && !jQuery._data( this[0], "parsedAttrs" ) ) { attr = this[0].attributes; for ( var i = 0, l = attr.length; i < l; i++ ) { name = attr[i].name; if ( name.indexOf( "data-" ) === 0 ) { name = jQuery.camelCase( name.substring(5) ); dataAttr( this[0], name, data[ name ] ); } } jQuery._data( this[0], "parsedAttrs", true ); } } return data; // 3. 參數(shù)key 是對象的情況,即參數(shù)格式是.data(key),則遍歷匹配元素集合,為每個匹配元素調(diào)用方法jQuery.data(elem, name, data,pvt)批量設(shè)置數(shù)據(jù) } else if ( typeof key === "object" ) { return this.each(function() { jQuery.data( this, key ); }); } // 4. 只傳入?yún)?shù)key的情況 如果只傳入?yún)?shù)key, 即參數(shù)格式是.data(key),則返回第一個匹配元素的指定名稱數(shù)據(jù) parts = key.split("."); parts[1] = parts[1] ? "." + parts[1] : ""; if ( value === undefined ) { data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); // Try to fetch any internally stored data first if ( data === undefined && this.length ) { data = jQuery.data( this[0], key ); data = dataAttr( this[0], key, data ); } return data === undefined && parts[1] ? this.data( parts[0] ) : data; // 5. 傳入?yún)?shù)key和value的情況 即參數(shù)格式是.data(key, value),則為每個匹配元素設(shè)置任意類型的數(shù)據(jù),并觸發(fā)自定義事件setData, changeData } else { return this.each(function() { var self = jQuery( this ), args = [ parts[0], value ]; self.triggerHandler( "setData" + parts[1] + "!", args ); jQuery.data( this, key, value ); self.triggerHandler( "changeData" + parts[1] + "!", args ); }); } }, removeData: function( key ) { return this.each(function() { jQuery.removeData( this, key ); }); } }); // 6. 函數(shù)dataAttr(elem, key, data)解析HTML5屬性data- function dataAttr( elem, key, data ) { // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute // 只有參數(shù)data為undefined時,才會解析HTML5屬性data- if ( data === undefined && elem.nodeType === 1 ) { var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); data = elem.getAttribute( name ); if ( typeof data === "string" ) { try { data = data === "true" ? true : data === "false" ? false : data === "null" ? null : jQuery.isNumeric( data ) ? parseFloat( data ) : rbrace.test( data ) ? jQuery.parseJSON( data ) : data; } catch( e ) {} // Make sure we set the data so it isn't changed later jQuery.data( elem, key, data ); } else { data = undefined; } } return data; }
五、$.removeData(elem, name),.removeData(key)
使用方法:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>jQuery.removeData demo</title> <style> div { margin: 2px; color: blue; } span { color: red; } </style> <script src="http://code.jquery.com/jquery-1.10.2.js"></script> </head> <body> <div>value1 before creation: <span></span></div> <div>value1 after creation: <span></span></div> <div>value1 after removal: <span></span></div> <div>value2 after removal: <span></span></div> <script> var div = $( "div" )[ 0 ]; $( "span:eq(0)" ).text( "" + $( "div" ).data( "test1" ) ); //undefined jQuery.data( div, "test1", "VALUE-1" ); jQuery.data( div, "test2", "VALUE-2" ); $( "span:eq(1)" ).text( "" + jQuery.data( div, "test1" ) ); // VALUE-1 jQuery.removeData( div, "test1" ); $( "span:eq(2)" ).text( "" + jQuery.data( div, "test1" ) ); // undefined $( "span:eq(3)" ).text( "" + jQuery.data( div, "test2" ) ); // value2 </script> </body> </html>
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>removeData demo</title> <style> div { margin: 2px; color: blue; } span { color: red; } </style> <script src="http://code.jquery.com/jquery-1.10.2.js"></script> </head> <body> <div>value1 before creation: <span></span></div> <div>value1 after creation: <span></span></div> <div>value1 after removal: <span></span></div> <div>value2 after removal: <span></span></div> <script> $( "span:eq(0)" ).text( "" + $( "div" ).data( "test1" ) ); // undefined $( "div" ).data( "test1", "VALUE-1" ); $( "div" ).data( "test2", "VALUE-2" ); $( "span:eq(1)" ).text( "" + $( "div").data( "test1" ) ); // VALUE-1 $( "div" ).removeData( "test1" ); $( "span:eq(2)" ).text( "" + $( "div" ).data( "test1" ) ); // undefined $( "span:eq(3)" ).text( "" + $( "div" ).data( "test2" ) ); // VALUE-2 </script> </body> </html>
$.removeData(elem, name),.removeData(key) 源碼解析:
$.extend({ // jQuery.removeData(elem,name,pvt)用于移除通過jQuery.data()設(shè)置的數(shù)據(jù) removeData: function( elem, name, pvt /* Internal Use Only */ ) { if ( !jQuery.acceptData( elem ) ) { return; } var thisCache, i, l, // Reference to internal data cache key internalKey = jQuery.expando, isNode = elem.nodeType, // See jQuery.data for more information cache = isNode ? jQuery.cache : elem, // See jQuery.data for more information id = isNode ? elem[ internalKey ] : internalKey; // If there is already no cache entry for this object, there is no // purpose in continuing if ( !cache[ id ] ) { return; } // 如果傳入?yún)?shù)name, 則移除一個或多個數(shù)據(jù) if ( name ) { thisCache = pvt ? cache[ id ] : cache[ id ].data; if ( thisCache ) { // 只有數(shù)據(jù)緩存對象thisCache存在時,才有必要移除數(shù)據(jù) // Support array or space separated string names for data keys if ( !jQuery.isArray( name ) ) { // try the string as a key before any manipulation if ( name in thisCache ) { name = [ name ]; } else { // split the camel cased version by spaces unless a key with the spaces exists name = jQuery.camelCase( name ); if ( name in thisCache ) { name = [ name ]; } else { name = name.split( " " ); } } } // 遍歷參數(shù)name中的數(shù)據(jù)名,用運算符delete逐個從數(shù)據(jù)緩存對象thisCache中移除 for ( i = 0, l = name.length; i < l; i++ ) { delete thisCache[ name[i] ]; } // If there is no data left in the cache, we want to continue // and let the cache object itself get destroyed if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { return; } } } // See jQuery.data for more information // 刪除自定義數(shù)據(jù)緩存對象cache[id].data if ( !pvt ) { delete cache[ id ].data; // Don't destroy the parent cache unless the internal data object // had been the only thing left in it if ( !isEmptyDataObject(cache[ id ]) ) { return; } } // Browsers that fail expando deletion also refuse to delete expandos on // the window, but it will allow it on all other JS objects; other browsers // don't care // Ensure that `cache` is not a window object #10080 // 刪除數(shù)據(jù)緩存對象cache[id] if ( jQuery.support.deleteExpando || !cache.setInterval ) { delete cache[ id ]; } else { cache[ id ] = null; } // We destroyed the cache and need to eliminate the expando on the node to avoid // false lookups in the cache for entries that no longer exist // 刪除DOM元素上擴展的jQuery.expando屬性 if ( isNode ) { // IE does not allow us to delete expando properties from nodes, // nor does it have a removeAttribute function on Document nodes; // we must handle all of these cases if ( jQuery.support.deleteExpando ) { delete elem[ internalKey ]; } else if ( elem.removeAttribute ) { elem.removeAttribute( internalKey ); } else { elem[ internalKey ] = null; } } } }); jQuery.fn.extend({ removeData: function( key ) { return this.each(function() { jQuery.removeData( this, key ); }); } }); // checks a cache object for emptiness function isEmptyDataObject( obj ) { for ( var name in obj ) { // if the public data object is empty, the private is still empty if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { continue; } if ( name !== "toJSON" ) { return false; } } return true; }
六、$.hasData(elem)
使用方法:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>jQuery.hasData demo</title> <script src="http://code.jquery.com/jquery-1.10.2.js"></script> </head> <body> <p>Results: </p> <script> var $p = jQuery( "p" ), p = $p[ 0 ]; $p.append( jQuery.hasData( p ) + " " ); // false $.data( p, "testing", 123 ); $p.append( jQuery.hasData( p ) + " " ); // true $.removeData( p, "testing" ); $p.append( jQuery.hasData( p ) + " " ); // false $p.on( "click", function() {} ); $p.append( jQuery.hasData( p ) + " " ); // true $p.off( "click" ); $p.append( jQuery.hasData( p ) + " " ); // false </script> </body> </html> $.hasData(elem) 源碼解析: $.extend({ hasData: function( elem ) { elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; return !!elem && !isEmptyDataObject( elem ); // 如果關(guān)聯(lián)的數(shù)據(jù)緩存對象存在,并且含有數(shù)據(jù),則返回true, 否則返回false。 這里用兩個邏輯非運算符! 把變量elem轉(zhuǎn)換為布爾值 } });
- 讀jQuery之六 緩存數(shù)據(jù)功能介紹
- Jquery中Ajax 緩存帶來的影響的解決方法
- jQuery ajax cache緩存問題
- jQuery 數(shù)據(jù)緩存data(name, value)詳解及實現(xiàn)
- 禁止JQuery中的load方法裝載IE緩存中文件的方法
- jQuery中ajax的使用與緩存問題的解決方法
- jquery 緩存問題的幾個解決方法
- IE下jquery ajax無法獲得最新數(shù)據(jù)的問題解決(IE緩存)
- jQuery數(shù)據(jù)緩存功能的實現(xiàn)思路及簡單模擬
- jQuery對象數(shù)據(jù)緩存Cache原理及jQuery.data方法區(qū)別介紹
- 關(guān)于jQuery對象數(shù)據(jù)緩存Cache原理以及jQuery.data詳解
- ie下jquery.getJSON的緩存問題的處理方法
相關(guān)文章
JQuery的html(data)方法與<script>腳本塊的解決方法
在使用Jquery的html(data)方法執(zhí)行寫數(shù)據(jù)到Dom元素時遇到一個問題:在data參數(shù)中包含script腳本塊的時候,html(data)方法的執(zhí)行結(jié)果與預(yù)期不符2010-03-03基于jQuery UI CSS Framework開發(fā)Widget的經(jīng)驗
jQuery UI CSS Framework是jQuery UI中的一個樣式框架,可以利用jQuery Theme roller 來生成自己想要的css樣式效果。我們可以利用jQuery UI的一些框架來開發(fā)出基于jQuery UI CSS Framework效果的插件來。2010-08-08jQuery Ajax 加載數(shù)據(jù)時異步顯示加載動畫
這篇文章主要介紹了jQuery Ajax 加載數(shù)據(jù)時異步顯示加載動畫的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-08-08jQuery插件zTree實現(xiàn)的基本樹與節(jié)點獲取操作示例
這篇文章主要介紹了jQuery插件zTree實現(xiàn)的基本樹與節(jié)點獲取操作,結(jié)合實例形式分析了jQuery樹形插件zTree構(gòu)造基本樹與針對節(jié)點的獲取操作相關(guān)實現(xiàn)技巧,需要的朋友可以參考下2017-03-03jQuery Attributes(屬性)的使用(二、類篇)
本系列文章主要講述jQuery框架的屬性(Attributes)使用方法,我將以實例方式進行講述,以簡單,全面為基礎(chǔ),不會涉及很深,我的學(xué)習(xí)方法:先入門,后進階!2009-12-12