jQuery中数据缓存$.data的用法及源码完全解析

一、实现原理:

对于DOM元素,通过分配一个唯一的关联id把DOM元素和该DOM元素的数据缓存对象关联起来,关联id被附加到以jQuery.expando的值命名的属性上,数据存储在全局缓存对象jQuery.cache中。在读取、设置、移除数据时,将通过关联id从全局缓存对象jQuery.cache中找到关联的数据缓存对象,然后在数据缓存对象上执行读取、设置、移除操作。

对于Javascript对象,数据则直接存储在该Javascript对象的属性jQuery.expando上。在读取、设置、移除数据时,实际上是对Javascript对象的数据缓存对象执行读取、设置、移除操作。

为了避免jQuery内部使用的数据和用户自定义的数据发生冲突,数据缓存模块把内部数据存储在数据缓存对象上,把自定义数据存储在数据缓存对象的属性data上。

二、总体结构:

// 数据缓存 Data jQuery.extend({ // 全局缓存对象 cache: {}, // 唯一 id种子 uuid:0, // 页面中每个jQuery副本的唯一标识 expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), // 是否有关联的数据 hasData: function(){}, // 设置、读取自定数据或内部数据 data: function(elem, name, data, pvt) {}, // 移除自定义数据或内部数据 removeData: function(elem, name, pvt) {}, // 设置、读取内部数据 _data: function(elem, name, data) {}, // 是否可以设置数据 acceptData: function(elem){} }); jQuery.fn.extend({ // 设置、读取自定义数据,解析HTML5属性data- data: function(key,value){}, // 移除自定义数据 removeData: function(key){} }); // 解析HTML5属性 data- function dataAttr(elem,key,data){} // 检查数据缓存对象是否为空 function isEmptyDataObject(obj){} jQuery.extend({ // 清空数据缓存对象 cleanData: function(elems){} });

三、$.data(elem, name, data), $.data(elem, name)
$.data(elem, name, data)的使用方法:
如果传入参数name, data, 则设置任意类型的数据

<!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="https://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, 未传入参数data, 则读取并返回指定名称的数据

<!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="https://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. 检查是否可以设置数据 if ( !jQuery.acceptData( elem ) ) { return; // 如果参数elem不支持设置数据,则立即返回 } // 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元素之间循环引用导致的浏览器(IE6/7)垃圾回收机制不起作用,要把数据存储在全局缓存对象jQuery.cache中;对于javascript对象,来及回收机制能够自动发生,不会有内存泄露的问题,因此数据可以查收存储在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. 如果是读取数据,但没有数据,则返回 if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { return; // getByName && data === undefined 如果name是字符串,data是undefined, 说明是在读取数据 // !id || !cache[id] || (!isEvents && !pvt && !cache[id].data 如果关联id不存在,说明没有数据;如果cache[id]不存在,也说明没有数据;如果是读取自动以数据,但cache[id].data不存在,说明没有自定义数据 } // 5. 如果关联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对象,关联id就是jQuery.expando } } // 6. 如果数据缓存对象不存在,则初始化为空对象{} 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对象,设置方法toJSON为空函数,以避免在执行JSON.stringify()时暴露缓存数据。如果一个对象定义了方法toJSON(),JSON.stringify()在序列化该对象时会调用这个方法来生成该对象的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. 如果参数name是对象或函数,则批量设置数据 if ( typeof name === "object" || typeof name === "function" ) { if ( pvt ) { cache[ id ] = jQuery.extend( cache[ id ], name ); // 对于内部数据,把参数name中的属性合并到cache[id]中 } else { cache[ id ].data = jQuery.extend( cache[ id ].data, name ); // 对于自定义数据,把参数name中的属性合并到cache[id].data中 } } // 8. 如果参数data不是undefined, 则设置单个数据 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 ] ) { // 如果参数name是字符串"events",并且未设置过自定义数据"events",则返回事件婚车对象,在其中存储了事件监听函数。 return privateCache.events; } // Check for both converted-to-camel and non-converted data property names // If a data property was specified //10. 如果参数name是字符串,则读取单个数据 if ( getByName ) { // First Try to find as-is property data ret = thisCache[ name ]; // 先尝试读取参数name对应的数据 // Test for null|undefined property data if ( ret == null ) { // 如果未取到,则把参数name转换为驼峰式再次尝试读取对应的数据 // Try to find the camelCased property ret = thisCache[ jQuery.camelCase( name ) ]; } } else { // 11. 如果未传入参数name,data,则返回数据缓存对象 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)
使用方法:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wzppgz.html