一、实现原理:

对于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-functiondataAttr(elem,key,data){}
// 检查数据缓存对象是否为空functionisEmptyDataObject(obj){}
jQuery.extend({
// 清空数据缓存对象 cleanData: function(elems){}
});

三、$.data(elem, name, data), $.data(elem, name)

$.data(elem, name, data)的使用方法:

如果传入参数name, data, 则设置任意类型的数据

<!doctype html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>jQuery.data demo</title>
<style>
div {
color: blue;
}
span {
color: red;
}
</style>
<scriptsrc="//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>
<htmllang="en">
<head>
<metacharset="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>
<scriptsrc="//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 ) ) {
case0 :
value = jQuery.data( div, "blah" );
break;
case1 :
jQuery.data( div, "blah", "hello" );
value = "Stored!";
break;
case2 :
jQuery.data( div, "blah", 86 );
value = "Stored!";
break;
case3 :
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 withnocache
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 foreach element since their data
// ends up in the globalcacheif ( 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
// cacheinorderto avoid key collisions between internal dataanduser-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. 特殊处理eventsif ( isEvents && !thisCache[ name ] ) { // 如果参数name是字符串"events",并且未设置过自定义数据"events",则返回事件婚车对象,在其中存储了事件监听函数。
return privateCache.events;
} // Checkforboth 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 useonly.
_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(); // 未传入参数

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. 未传入参数的情况if ( typeof key === "undefined" ) {
if ( this.length ) { // 如果参数key是undefined, 即参数格式是.data(), 则调用方法jQuery.data(elem, name, data, 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], name, data[ name ] );
}
}
jQuery._data( this[0], "parsedAttrs", true );
}
} return data;
// 3. 参数key 是对象的情况,即参数格式是.data(key),则遍历匹配元素集合,为每个匹配元素调用方法jQuery.data(elem, name, data,pvt)批量设置数据
} elseif ( typeof key === "object" ) {
returnthis.each(function() {
jQuery.data( this, key );
});
}
// 4. 只传入参数key的情况 如果只传入参数key, 即参数格式是.data(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 firstif ( 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. 传入参数key和value的情况 即参数格式是.data(key, value),则为每个匹配元素设置任意类型的数据,并触发自定义事件setData, changeData
} else {
returnthis.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 ) {
returnthis.each(function() {
jQuery.removeData( this, key );
});
}
}); // 6. 函数dataAttr(elem, key, data)解析HTML5属性data-functiondataAttr( elem, key, 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, key, data ); } else {
data = undefined;
}
} return data;
}

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

使用方法:

<!doctype html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>jQuery.removeData demo</title>
<style>
div {
margin: 2px;
color: blue;
}
span {
color: red;
}
</style>
<scriptsrc="//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>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>removeData demo</title>
<style>
div {
margin: 2px;
color: blue;
}
span {
color: red;
}
</style>
<scriptsrc="//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()设置的数据
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 continuingif ( !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 keysif ( !jQuery.isArray( name ) ) { // try the string as a key before any manipulationif ( 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 destroyedif ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
return;
}
}
} // See jQuery.data for more information// 删除自定义数据缓存对象cache[id].dataif ( !pvt ) {
delete cache[ id ].data; // Don't destroy the parent cache unless the internal data object// had been the only thing left in itif ( !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 casesif ( jQuery.support.deleteExpando ) {
delete elem[ internalKey ];
} elseif ( elem.removeAttribute ) {
elem.removeAttribute( internalKey );
} else {
elem[ internalKey ] = null;
}
}
}
}); jQuery.fn.extend({
removeData: function( key ) {
returnthis.each(function() {
jQuery.removeData( this, key );
});
}
}); // checks a cache object for emptinessfunctionisEmptyDataObject( obj ) {
for ( var name in obj ) { // if the public data object is empty, the private is still emptyif ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
continue;
}
if ( name !== "toJSON" ) {
return false;
}
} return true;
}

六、$.hasData(elem)

使用方法:

<!doctype html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>jQuery.hasData demo</title>
<scriptsrc="//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 );
// 如果关联的数据缓存对象存在,并且含有数据,则返回true, 否则返回false。 这里用两个逻辑非运算符! 把变量elem转换为布尔值
}
});

jQuery数据缓存$.data 的使用以及源码解析的更多相关文章

  1. 【Android初级】利用startActivityForResult返回数据到前一个Activity(附源码+解析)

    在Android里面,从一个Activity跳转到另一个Activity.再返回,前一个Activity默认是能够保存数据和状态的.但这次我想通过利用startActivityForResult达到相 ...

  2. jquery源码解析:jQuery数据缓存机制详解1

    jQuery中有三种添加数据的方法,$().attr(),$().prop(),$().data().但是前面两种是用来在元素上添加属性值的,只适合少量的数据,比如:title,class,name等 ...

  3. [源码解析] PyTorch 分布式(1) --- 数据加载之DistributedSampler

    [源码解析] PyTorch 分布式(1) --- 数据加载之DistributedSampler 目录 [源码解析] PyTorch 分布式(1) --- 数据加载之DistributedSampl ...

  4. [源码解析] PyTorch 分布式(2) --- 数据加载之DataLoader

    [源码解析] PyTorch 分布式(2) --- 数据加载之DataLoader 目录 [源码解析] PyTorch 分布式(2) --- 数据加载之DataLoader 0x00 摘要 0x01 ...

  5. MyBatis源码解析之数据源(含数据库连接池简析)

    一.概述: 常见的数据源组件都实现了javax.sql.DataSource接口: MyBatis不但要能集成第三方的数据源组件,自身也提供了数据源的实现: 一般情况下,数据源的初始化过程参数较多,比 ...

  6. Laravel源码解析之model(代码)

    本篇文章给大家带来的内容是关于Laravel源码解析之model(代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 前言 提前预祝猿人们国庆快乐,吃好.喝好.玩好,我会在电视上看 ...

  7. jquery源码解析:jQuery数据缓存机制详解2

    上一课主要讲了jQuery中的缓存机制Data构造方法的源码解析,这一课主要讲jQuery是如何利用Data对象实现有关缓存机制的静态方法和实例方法的.我们接下来,来看这几个静态方法和实例方法的源码解 ...

  8. JQuery源码解析(九)

    jQuery回调对象 jQuery.Callbacks一般开发者接触的很少,虽然jQuery向开发者提供了外部接口调用,但是$.Callbacks()模块的开发目的是为了给内部$.ajax() 和 $ ...

  9. jQuery源码解析资源便签

    最近开始解读jQuery源码,下面的链接都是搜过来的,当然妙味课堂 有相关的一系列视频,长达100多期,就像一只蜗牛慢慢爬, 至少品读三个框架,以后可以打打怪,自己造造轮子. 完全理解jQuery源代 ...

随机推荐

  1. 磁盘磁盘MBR与GPT的区别

    基本磁盘与动态磁盘    磁盘的使用方式可以分为两类:一类是“基本磁盘”.基本磁盘非常常见,我们平时使用的磁盘类型基本上都是“基本磁盘”.“基本磁盘”受26个英文字母的限制,也就是说磁盘的盘符只能是2 ...

  2. Beginning and Ending the Speech

    Beginning and Ending the Speech Just as musical plays need appropriate beginnings and endings, so do ...

  3. 安装ubuntu server时候的多网卡问题

    安装的时候看到多个网卡,eth0,eth1,到系统中后只看见eth0 1.输入 ifconfig -a,这个时候如果能够看到多网卡,则在/etc/network/.interfaces中配置一下网卡就 ...

  4. java中引用

    java中引用分为,强,弱,虚,软 (1)强引用 使用最普遍的引用.如果一个对象具有强引用,它绝对不会被gc回收.如果内存空间不足了,gc宁愿抛出OutOfMemoryError,也不是会回收具有强引 ...

  5. mysql5.1解压版安装

    1.如果已经安装别的版本先卸载干净,cmd管理员权限登录,mysql\bin目录:mysqld -remove 2.环境变量 MYSQL:path 3.my.ini [mysqld]port=3306 ...

  6. rm: cannot remove `xxx’: Operation not permitted问题的处理方案

    第一步:22.txt lsattr 22.txt 查看文件属性 看到的情况 -----a------- 第二步:去除a的属性 chattr -a 22.txt 第三步:在此执行删除 rm 22.txt

  7. Codeforces - 331B2 权值线段树 区间合并

    题意:题目太玄了我无法用语言精简.. 题目要求的操作1是基于值的,所以用普通线段树基本无法维护(反正我不知道) 换做权值型后十分好做,因为连接处必然是更后面的,这时比较一下位置就好 PS.感觉周赛越来 ...

  8. 剑指offer——面试题22:链表中倒数第k个节点

    注意代码的鲁棒性! 函数: ListNode* TheLastKthNode(ListNode* pHead,int k) { ) return nullptr; ListNode* quickNod ...

  9. 最受欢迎的5款Node.js端到端测试框架

    测试,尤其是自动化测试在现代 WEB 工程中有着非常重要的角色,与交付过程集成良好的自动化测试流程可以在新版发布时帮你快速回归产品功能,也可以充当产品文档.测试因粒度不同又可以分为单元测试.接口测试. ...

  10. 1-----Docker实例-安装Nginx

    Docker 安装 Nginx 方法一.docker pull nginx(推荐) 查找 Docker Hub 上的 nginx 镜像 runoob@runoob:~/nginx$ docker se ...