首页 > 代码库 > jQuery data
jQuery data
一个简单的Cache
(function(){ var __cache = {}, Cache = { get: function(__name){ return __cache[__name] || undefined; }, set: function(__name, __value){ return (__cache[__name] = __value) } }; this.Cache = Cache; })(); alert(Cache.get("name")); //undefined Cache.set("name", "Bob"); alert(Cache.get("name")); //Bob
但这不是jQuery想要的
jQuery要解决的是对应元素的缓存数据。
例如,我们用document.getElementById获得了一个元素element,然后有一个对应的参数value的属性名是key,那么我们想保存到缓存里,那么我们需要告诉缓存element、key、value才能保存数据,而想要获得这个值,则要告诉缓存element和key,才能得到value。
所以jQuery的缓存实际上是直接绑定到对象中的。
为什么?因为这样简单啊。
用上面的方法,先要将element转成字符串或者数字对应缓存里的对象,然后再用该对象来缓存不同key的value……这……太……麻……烦……了!!
实际上,由于Javascript没有Hash值方法,所以对象转字符串或数字并没有太好的方法,当然绑一个ID在元素上除外。
做一个别人一般不会用的令牌
但是绑定在对象上有一个问题,如果属性名用什么呢?
如果这个属性名别人也拿去用就悲剧了,比如我用.cache绑定数据,但是另一个库也有.cache来绑定数据,就……
所以,jQuery做了一个正常情况下别人不会用的令牌。
jQuery.expando = "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" );
replace函数将core_verision中的非数字全部替换掉,所以最后这个令牌是一个jQuery后面加一个随机数,比如:
jQuery20018518865841457738
jQuery.hasData
jQuery.hasData = http://www.mamicode.com/function( elem ) {>
从这个函数可以看出,如果elem是DOM Element对象,则数据存在jQuery.cahe中,否则存在存在elem对象中。
jQuery.data & jQuery.removeData
jQuery.data = http://www.mamicode.com/function( elem, name, data ) {>
jQuery.removeData = http://www.mamicode.com/function( elem, name ) {>
他们分别调用了internalData和internalRemoveData。
注意专用接口jQuery._data和jQuery._removeData传的最后一个值有些不同。
这个后面会说到。
jQuery._data = http://www.mamicode.com/function( elem, name, data ) {>
jQuery._removeData = http://www.mamicode.com/function( elem, name ) {>
internalData
function internalData( elem, name, data, pvt /* Internal Use Only */ ){ // 判断该对象能不能绑定数据 if ( !jQuery.acceptData( elem ) ) { return; } var thisCache, ret, internalKey = jQuery.expando, getByName = typeof name === "string", // 由于IE6-7的DOM节点引用的垃圾回收问题,需要分开处理DOM节点和JS对象 // 真心想吐槽,这不是jQuery 2.0么!!!不是说不支持IE6-8么!!! isNode = elem.nodeType, // 如果是DOM节点,则使用jQuery.cache存储数据,否则使用elem本身 cache = isNode ? jQuery.cache : elem, // 得到对象的ID号,如果是DOM节点则是其以令牌为属性名的属性值,否则是令牌 id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; // 避免为了从一个根本没有数据的对象获取数据而浪费时间 if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data =http://www.mamicode.com/== undefined ) {"object" || typeof name === "function" ) { // 如果是jQuery内部私用数据 if ( pvt ) { // 则将数据保存在指定ID的对应缓存中 cache[ id ] = jQuery.extend( cache[ id ], name ); } else { //否则保存在指定ID对应缓存的data属性中 cache[ id ].data = http://www.mamicode.com/jQuery.extend( cache[ id ].data, name );>
jQuery 2.0中data的实现依然同1.9版本差不多,当然这也不一定是IE6-7的原因才将DOM节点和JS对象分开处理的,我们知道JS引擎读取DOM数据的过程是较为费时费力的,从这个角度来看,将DOM节点的缓存设计在全局会是个比较快的方案。
这里还有两个有趣的问题:
- 如果传进去的data是函数,那么到底缓存了什么?
- 为什么用户数据在data中,而内部数据直接在缓存对象里,而不是反过来呢?
internalRemoveData
function internalRemoveData( elem, name, pvt /* For internal use only */ ){ // 判断该对象能不能绑定数据 if ( !jQuery.acceptData( elem ) ) { return; } var thisCache, i, l, isNode = elem.nodeType, cache = isNode ? jQuery.cache : elem, id = isNode ? elem[ jQuery.expando ] : jQuery.expando; // 如果缓存对象根本不存在,那么就不用删除了 if ( !cache[ id ] ) { return; } // 如果name存在 if ( name ) { // 定位缓存位置 thisCache = pvt ? cache[ id ] : cache[ id ].data; // 如果缓存存在 if ( thisCache ) { // 支持以空格分隔的字符串 if ( !jQuery.isArray( name ) ) { // try the string as a key before any manipulation // 看看字符串name存不存在 if ( name in thisCache ) { // 定位要删除的缓存 name = [ name ]; // 不存在证明传进来的是以空格分隔的字符串或者需要转成驼峰写法 } else { //转成驼峰写法 name = jQuery.camelCase( name ); //看看现在对不对 if ( name in thisCache ) { name = [ name ]; //不对证明是以空格分隔的字符串 } else { //以空格分隔字符串 name = name.split(" "); } } } else { // 如果是数组,则预处理 name = name.concat( jQuery.map( name, jQuery.camelCase ) ); } // 遍历删除 for ( i = 0, l = name.length; i < l; i++ ) { delete thisCache[ name[i] ]; } // 如果缓存非空,则退出,证明缓存如果都空了,就要删掉它 if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { return; } } } // 如果不是私有的 if ( !pvt ) { // 删除data属性 delete cache[ id ].data; // 如果缓存对象并非空的,证明可能还有些私有属性存储了,退出 if ( !isEmptyDataObject( cache[ id ] ) ) { return; } } // 到这里已经所有缓存数据都没有了,可以清理ID之类的东西了 // 如果是DOM节点 if ( isNode ) { // 清理数据 jQuery.cleanData( [ elem ], true ); // 看看能不能用delete方法删除 // 还要判断cache本身是不是window对象,否则会抛错 } else if ( jQuery.support.deleteExpando || cache != cache.window ) { delete cache[ id ]; // 如果不能删除则设为null } else { cache[ id ] = null; } }
jQuery.fn.data
jQuery.fn.data = http://www.mamicode.com/function( key, value ) {"parsedAttrs" ) ) { // 从attrubutes中获取数据 attrs = elem.attributes; // 遍历 for ( ; i < attrs.length; i++ ) { name = attrs[i].name; // 看看属性名是不是data-xxx,就是要支持HTML5 data-Attributes if ( !name.indexOf( "data-" ) ) { // 是则数据名为data-后面的字符串 name = jQuery.camelCase( name.slice(5) ); // 数据值通过daataAttr获取 dataAttr( elem, name, data[ name ] ); } } // 保存到对应内部缓存parsedAttrs中 jQuery._data( elem, "parsedAttrs", true ); } } return data; } // 如果key是对象,则通过jQuery.data设置多个属性 if ( typeof key === "object" ) { return this.each(function() { jQuery.data( this, key ); }); } // 否则用access操作链式或不用链式 return jQuery.access( this, function( value ) { // 如果value没有定义,则是读取操作 if ( value =http://www.mamicode.com/== undefined ) {>
这个方法主要是支持了HTML5 data-Attributes。
我们可以发现,实际上jQuery最终也把data-Attributes数据也保存到缓存中,这样是为了不再DOM和JS引擎中频繁读取。
jQuery.fn.removeData
jQuery.fn.removeData = http://www.mamicode.com/function( key ) {>
这个就很简单法了,只是遍历所有元素使用removeData而已。
dataAttr
function dataAttr( elem, key, data ) { // 从data-*获取数据 if ( data =http://www.mamicode.com/== undefined && elem.nodeType === 1 ) {"data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); // 尝试获取该attribute的数据 data = http://www.mamicode.com/elem.getAttribute( name );"string" ) { try { // 如果数据是"true",则为true data = http://www.mamicode.com/data ==="true" ? true : // 如果数据是"false",则为false data =http://www.mamicode.com/=="false" ? false : // 如果数据时"null",则为null data =http://www.mamicode.com/=="null" ? null : // 将字符串转成数字,再转成字符串看看有没有改变,没改变则证明是数字 +data + "" === data ? +data : // 否则测试数据是否是以{}包裹,是则尝试转成对象 rbrace.test( data ) ? jQuery.parseJSON( data ) : //否则就当它是普通字符串 data; } catch( e ) {} //保存数据 jQuery.data( elem, key, data ); // 否则数据未定义 } else { data = http://www.mamicode.com/undefined;>
jQuery data