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

前端之家收集整理的这篇文章主要介绍了jQuery中数据缓存$.data的用法及源码完全解析前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

一、实现原理:

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

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

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

二、总体结构:

页面中每个jQuery副本的唯一标识 expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g,"" ),// 是否有关联的数据 hasData: function(){},// 设置、读取自定数据或内部数据 data: function(elem,name,data,pvt) {},// 移除自定义数据或内部数据 removeData: function(elem,// 设置、读取内部数据 _data: function(elem,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,data),$.data(elem,name)

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

<Meta charset="utf-8"> jQuery.data demo

$.data(elem,name)的使用方法: 如果传入key,未传入参数data,则读取并返回指定名称的数据

<Meta charset="utf-8"> jQuery.data demo

$.data(elem,name) 源码解析:

不支持设置数据,则立即返回
  }

// 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,则返回数据缓存对象
ret = thisCache;
}

return ret;
},// For internal use only.
_data: function( elem,data ) {
return jQuery.data( elem,true );
},});

四、.data(key,value),.data(key)

使用方法

HTML5 data attriubutes:

$( "div" ).data( "role" ) === "page";
$( "div" ).data( "lastValue" ) === 43;
$( "div" ).data( "hidden" ) === true;
$( "div" ).data( "options" ).name === "John";

.data(key,.data(key) 源码解析

// 2. 未传入参数的情况
if ( typeof key === "undefined" ) {
if ( this.length ) { // 如果参数key是undefined,即参数格式是.data(),则调用方法jQuery.data(elem,pvt)获取第一个匹配元素关联的自定义数据缓存对象,并返回。
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],data[ name ] );
  }
 }
 jQuery._data( this[0],"parsedAttrs",true );
}

}

return data;
// 3. 参数key 是对象的情况,即参数格式是.data(key),则遍历匹配元素集合,为每个匹配元素调用方法jQuery.data(elem,pvt)批量设置数据
} else if ( typeof key === "object" ) {
return this.each(function() {
jQuery.data( this,key );
});
}
// 4. 只传入参数key的情况 如果只传入参数key,则返回第一个匹配元素的指定名称数据
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],data );
}

return data === undefined && parts[1] ?
this.data( parts[0] ) :
data;

// 5. 传入参数key和value的情况 即参数格式是.data(key,则为每个匹配元素设置任意类型的数据,并触发自定义事件setData,changeData
} else {
return this.each(function() {
var self = jQuery( this ),args = [ parts[0],value ];

self.triggerHandler( "setData" + parts[1] + "!",args );
jQuery.data( this,value );
self.triggerHandler( "changeData" + parts[1] + "!",args );

});
}
},removeData: function( key ) {
return this.each(function() {
jQuery.removeData( this,key );
});
}
});

// 6. 函数dataAttr(elem,data)解析HTML5属性data-
function dataAttr( elem,data ) {
// If nothing was found internally,try to fetch any
// data from the HTML5 data-* attribute
// 只有参数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,data );

} else {
data = undefined;
}
}

return data;
}

五、$.removeData(elem,name),.removeData(key)

使用方法

<Meta charset="utf-8"> jQuery.removeData demo
<Meta charset="utf-8"> removeData demo

$.removeData(elem,name),.removeData(key) 源码解析:

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;
}

// 如果传入参数name,则移除一个或多个数据

if ( name ) {

thisCache = pvt ? cache[ id ] : cache[ id ].data;

if ( thisCache ) { // 只有数据缓存对象thisCache存在时,才有必要移除数据

// 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( " " );
  }
 }
}

// 遍历参数name中的数据名,用运算符delete逐个从数据缓存对象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
// 删除自定义数据缓存对象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
// 删除数据缓存对象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)

使用方法

<Meta charset="utf-8"> jQuery.hasData demo $.hasData(elem) 源码解析: $.extend({ hasData: function( elem ) { elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; return !!elem && !isEmptyDataObject( elem ); // 如果关联的数据缓存对象存在,并且含有数据,则返回true,否则返回false。 这里用两个逻辑非运算符! 把变量elem转换为布尔值 } });
原文链接:https://www.f2er.com/jquery/49067.html

猜你在找的jQuery相关文章