关于jQuery对象数据缓存Cache原理以及jQuery.data详解


Posted in Javascript onApril 07, 2013

网上有很多教你怎么使用jQuery.data(..)来实现数据缓存,但有两个用户经常使用的data([key],[value])和jQuery.data(element,[key],[value])几乎没有什么文章说清楚它们两的区别,所以我用到了,研究下分享给大家。
$("").data([key],[value])与jQuery.data(element,[key],[value])的区别
这两个函数都是用来在元素上存放数据也就平时所说的数据缓存,都返回jQuery对象,当时我分别在使用它俩的时候真的吓我一跳,区别可大了,真是不用不知道,一用吓一跳。看例子先吧,后再根据源代码分析。

Js代码:
<div id="test2" onclick="test()">test2</div> 
<div id="abc3" onclick="test()">test3</div> 
<div id="test" onclick="test()">test</div> 
<p id="ttt">aaaa</p> 
<script> 
$(document).ready(function(){ 
$("#test").click(function(){ 
alert("JQUERY"); var e=$("div");//定义了两jquery对象 
var w=$("div");//e是不等于w的。 
//首先使用data([key],[value])用法。 
$(e).data("a","aaaa");//分别在e和w上保存Key一样的数据, 
$(w).data("a","wwww");// 看它是否会覆盖前面的,虽然是保存在不同对象上。 
alert($(e).data("a"));//你猜到答案了吗,里输出是wwww;是不是有点意外? 
alert(e===w)//false 
alert($(w).data("a"));//这里也是wwww; 
//使用jQuery.data(element,[key],[value])来存放数据。 
$.data(e,"b","cccc");//分别在e和w上保存Key一样的数据, 
$.data(w,"b","dddd");// 看它是否会覆盖前面的,虽然是保存在不同对象上。 
alert($.data(e,"b"));//应该你能猜答案吧,输出cccc 
alert($.data(w,"b"));//这输出dddd 
}); 
}); 
</script>

看了上面的例子是不是发现data([key],[value])与jQuery.data(element,[key],[value])两个根本就不一样了对吧?它们之间到底有没有关系呢。怎么data([key],[value])会覆盖前面key相同的值呢?
而jQuery.data(element,[key],[value])只要是绑定到不同的对象上都不会造成覆盖。是这样吗?那来研究下它们的源代码吧。
先看jQuery.data(element,[key],[value])源代码。

Js代码:

jQuery.extend({ 
cache: {}, // Please use with caution 
uuid: 0, 
// Unique for each copy of jQuery on the page 
// Non-digits removed to match rinlinejQuery 
expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), 
.... 
data: function( elem, name, data, pvt /* Internal Use Only */ ) { 
// 是否可以附加数据,不可以则直接返回 
if ( !jQuery.acceptData( elem ) ) { 
return; 
} 
var privateCache, thisCache, ret, 
//jQuery.expando这是一个唯一的字符串,是这介jquery对象产生的时候就生成了。 
internalKey = jQuery.expando, 
getByName = typeof name === "string", 
// 必须区分处理DOM元素和JS对象,因为IE6-7不能垃圾回收对象跨DOM对象和JS对象进行的引用属性 
isNode = elem.nodeType, 
// 如果是DOM元素,则使用全局的jQuery.cache 
// 如果是JS对象,则直接附加到对象上 
cache = isNode ? jQuery.cache : elem, 
// 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"; 
// 避免做更多的不必要工作,当尝试在一个没有任何数据的对象上获取数据时 
// 对象没有任何数据,直接返回 
if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { 
return; 
} 
// 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 ) { 
// 如果是DOM元素则在元素上产生唯一的ID 并且以jQuery.expando 
//为属性值为id保存在elem元素上,以便以后再根据jQuery.expando来查找ID。 
elem[ internalKey ] = id = ++jQuery.uuid; 
} else { 
// JS对象则直接使用jQuery.expando,既然是直接附加到对象上,又何必要id呢? 
// 避免与其他属性冲突! 
id = internalKey; 
} 
} 
//// 当我们试着访问一个键是否含有值的时候,如果不存在jQuery.cache[id]值, 
// 初始化jQuery.cache[id]值 为一个空对象{} 
if ( !cache[ id ] ) { 
cache[ id ] = {}; 
if ( !isNode ) { 
cache[ id ].toJSON = jQuery.noop; 
} 
} 
// An object can be passed to jQuery.data instead of a key/value pair; this gets 
// shallow copied over onto the existing cache 
// data是接收对象和函数,浅拷贝 
if ( typeof name === "object" || typeof name === "function" ) { 
if ( pvt ) { 
cache[ id ] = jQuery.extend( cache[ id ], name ); 
} else { 
cache[ id ].data = jQuery.extend( cache[ id ].data, name ); 
} 
} 
/ 存储对象,存放了所有数据的映射对象 
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. 
// jQuery内部数据存在一个独立的对象(thisCache.data==thisCache[ internalKey ]) 
//上,为了避免内部数据和用户定义数据冲突 
if ( !pvt ) { 
// 存放私有数据的对象不存在,则创建一个{} 
if ( !thisCache.data ) { 
thisCache.data = {}; 
} 
// 使用私有数据对象替换thisCache 
thisCache = thisCache.data; 
} 
// 如果data不是undefined,表示传入了data参数,则存储data到name属性上 
if ( data !== undefined ) { 
// jQuery.camelCase( name )作用是如果传入的是object/function,不做转换, 
//只有传入的name是字符串才会转换。所以最终保存下来的是key/value对; 
thisCache[ jQuery.camelCase( name ) ] = data; 
} 
//从这以后下面的代码都是处理data: function( elem, name)data为空,求返回值data的情况了。 
if ( isEvents && !thisCache[ name ] ) { 
return privateCache.events; 
} 
// 如果name是字符串,则返回data 
// 如果不是,则返回整个存储对象 
if ( getByName ) { 
// First Try to find as-is property data 
ret = thisCache[ name ]; 
// Test for null|undefined property data 
if ( ret == null ) { 
// Try to find the camelCased property 
ret = thisCache[ jQuery.camelCase( name ) ]; 
} 
} else { 
ret = thisCache; 
} 
return ret; 
}, 
............ 
});

请看图。关于jQuery对象数据缓存Cache原理以及jQuery.data详解

看jQuery.data(element,[key],[value])源代码后可以知道,每一个element都会有自己的一个{key:value}对象保存着数据,所以新建的对象就算有key相同它也不会覆盖原来存在的对象key所对应的value,因为新对象保存是是在另一个{key:value}对象中。

接下来要分析data([key],[value])源代码使用到了each(callback),在分析它之前先看下each(callback)用法和源代码。

Js代码:
<div id="test2" onclick="test()">test2</div> 
<div id="abc3" onclick="test()">test3</div> 
<div id="test" onclick="test()">test</div> 
<p id="ttt">aaaa</p> 
<script> 
$(document).ready(function(){ 
$("#test").click(function(){ 
alert("JQUERY"); var i=0; 
$("#abc3").each(function() { 
alert(++i);//只输出1;因为只有一个<div id="abc3"> 
}); 
alert("----"); 
var j=0; 
$("div").each(function() { 
alert(++j);//分别输出1,2,3;因为有三个<div>所以循环三遍 
}); 
}); 
}); 
</script> 
现在来看each方法的具体实现如下: 
jQuery.fn = jQuery.prototype = { 
each: function( callback, args ) { 
return jQuery.each( this, callback, args ); 
} 
} 
可以看到它返回的是全局的each方法,并且将自身jQuery对象做为参数给它,全局的each方法的具体实现如下: 
// args 作为内部成员的调用来使用 
each: function( object, callback, args ) { 
var name, i = 0, length = object.length; // 当object为jQuery对象时,length非空 
if ( args ) { 
if ( length === undefined ) { 
for ( name in object ) 
if ( callback.apply( object[ name ], args ) === false ) 
break; 
} else 
for ( ; i < length; ) 
if ( callback.apply( object[ i++ ], args ) === false ) 
break; 
// 以下是客户端程序进行调用 
} else { 
if ( length === undefined ) { 
for ( name in object ) 
if ( callback.call( object[ name ], name, object[ name ] ) === false ) 
break; 
} else 
// i表示索引值,value表示DOM元素 
for ( var value = object[0]; 
i < length && callback.call( value, i, value ) !== false; 
value = object[++i] ){} 
} 
return object; 
}

现在我们关注下 for ( var value = object[0]; i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} 这句代码;其中object[0]取得jQuery对象中的第一个DOM元素,通过for循环,
得到遍历整个jQuery对象中对应的每个DOM元素,通过callback.call( value,i,value); 将callback的this对象指向value对象,并且传递两个参数,i表示索引值,value表示DOM元素;其中callback是类似于 function(index, elem) { } 的方法。所以就得到 $("").each(function(index, elem){ });

再来看看data([key],[value])的源代码

Js代码:
jQuery.fn.extend({ 
data: function( key, value ) { 
var parts, part, attr, name, l, 
elem = this[0], 
i = 0, 
data = null; // Gets all values 
if ( key === undefined ) { 
.....//处理没有Key的情况,这里不是我们要讨论的
return data; 
} 
// Sets multiple values 
if ( typeof key === "object" ) { 
return this.each(function() { 
jQuery.data( this, key ); 
}); 
} 
parts = key.split( ".", 2 ); 
parts[1] = parts[1] ? "." + parts[1] : ""; 
part = parts[1] + "!"; 
return jQuery.access( this, function( value ) { 
if ( value === undefined ) { 
。。。//这里是没有value时,是索取返回值的情况,这不是我们讨论 
} 
parts[1] = value; 
//如果我使用用$("div").data("a","aaa")),下面调用each前的this指的是$("div")这返回的对象, 
this.each(function() {//注意了,这里是以每一个匹配的元素作为上下文来执行一个函数 
var self = jQuery( this ); 
self.triggerHandler( "setData" + part, parts ); 
//这里在元素上存放数据,本质还是委托data(element,[key],[value])来做的。 
//看前面有分析过了。 
//下面data( this, key, value )里的this指的是遍历整个jQuery对象中对应的每个DOM元素 
//$("div")它对应页面中一个<div>数组。 
jQuery.data( this, key, value )<span style="background-color: #ffcc00;">;//这名句会被循环多次执行,也就是保存数据</span>。 
//这里就是核心一句话。但要清楚看上面了它是在each(functipn(){})中的。 
self.triggerHandler( "changeData" + part, parts ); 
}); 
}, null, value, arguments.length > 1, null, false ); 
}, 
//在元素上移除存放的数据。具体实现如下: 
removeData: function( key ) { 
return this.each(function() { 
jQuery.removeData( this, key ); 
}); 
} 
});

如果对于data([key],[value])的源代码不是很了解,好吧,我就用一个例子来模仿实现它吧。

Js代码:
<div id="test2" onclick="test()">test2</div> 
<div id="abc3" onclick="test()">test3</div> 
<div id="test" onclick="test()">test</div> 
<p id="ttt">aaaa</p> 
<script> 
$(document).ready(function(){ 
$("#test").click(function(){ 
alert("JQUERY"); var i=0; 
$("#abc3").each(function() { 
alert(++i);//只输出1;因为只有一个<div id="abc3"> 
}); 
alert("----"); 
var j=1; 
$("div").each(function() {//以每一个匹配的元素作为上下文来执行这个函数 
$.data(this,"a","wwww");//这里的this就是指$("div"), 
//分别遍历每一个匹配的元素给它们每一个对象{}都保存一个key/value 
alert(j++);//分别输出1 ,2 ,3 因为有三个<div>元素 
}); 
alert($("#test").data("a"));//返回wwww, 
//是不是很惊呀,我没有保存在它身上啊,怎么也有值,很明显是它是查这个div节点上有没有, 
//肯定是有值了,因为上面给循环保存在div这Dom结点上了。 
alert($("#test")===$("div"));//false证明两新建的对象不是同一个。 
alert($("div").data("a"));//返回wwww, 
//这里也是一样因为是div节点上都保存了"a"="wwww"这样一个键值对了。 
}); 
}); 
</script>

现在对data([key],[value])与jQuery.data(element,[key],[value])都有了解了吧,如果还是半懂,再回头多看一遍,耐心地理解一下。其实表面上很不一样。但本质上还是有联系的,现在明白原理后就可以请放心地使用了。jQuery.data(element,[key],[value])只把数据绑定到参数element节点上。data([key],[value])
如$("div").data("a","aaaa")它是把数据绑定每一个匹配div节点的元素上。
附加说明下,文中所分析用到的是 jquery-1.7.2.js的源代码。下载地址:http://demo.3water.com/jslib/jquery/jquery-1.7.2.min.js

 

Javascript 相关文章推荐
document.all与WEB标准
May 13 Javascript
javascript 获取select下拉列表值的代码
Sep 07 Javascript
js 数组克隆方法 小结
Mar 20 Javascript
js借助ActiveXObject实现创建文件
Sep 29 Javascript
js 3种归并操作的实例代码
Oct 30 Javascript
JavaScript中连接操作Oracle数据库实例
Apr 02 Javascript
vue-cli2.9.3 详细教程
Apr 23 Javascript
实例详解Vue项目使用eslint + prettier规范代码风格
Aug 20 Javascript
微信小程序ibeacon三点定位详解
Oct 31 Javascript
jQuery实现ajax的嵌套请求案例分析
Feb 16 jQuery
JavaScript数组去重的方法总结【12种方法,号称史上最全】
Feb 28 Javascript
element-ui和vue表单(对话框)验证提示语(残留)清除操作
Sep 11 Javascript
有关于eclipse配置spket需要注意的一些地方
Apr 07 #Javascript
如何在一个页面显示多个百度地图
Apr 07 #Javascript
关于JS字符串函数String.replace()
Apr 07 #Javascript
有关于JS构造函数的重载和工厂方法
Apr 07 #Javascript
JS完成代码前最好对其做5件事
Apr 07 #Javascript
有关于JS辅助函数inherit()的问题
Apr 07 #Javascript
运算符&amp;&amp;的三个不同层次
Apr 07 #Javascript
You might like
php图片上传存储源码并且可以预览
2011/08/26 PHP
PHP实现下载功能的代码
2012/09/29 PHP
PHP处理Oracle的CLOB实例
2014/11/03 PHP
PHP变量赋值、代入给JavaScript中的变量
2015/06/29 PHP
JS判断是否为数字,是否为整数,是否为浮点数的代码
2010/04/24 Javascript
Array.prototype.concat不是通用方法反驳[译]
2012/09/20 Javascript
jQuery事件绑定和委托实例
2014/11/25 Javascript
JavaScript正则获取地址栏中参数的方法
2017/03/02 Javascript
angularjs中的$eval方法详解
2017/04/24 Javascript
javascript过滤数组重复元素的实现方法
2017/05/03 Javascript
详解使用vuex进行菜单管理
2017/12/21 Javascript
详解vue-cli3多环境打包配置
2019/03/28 Javascript
vue使用axios实现excel文件下载的功能
2020/07/16 Javascript
uniapp微信小程序:key失效的解决方法
2021/01/20 Javascript
python爬虫获取京东手机图片的图文教程
2017/12/29 Python
python+selenium实现163邮箱自动登陆的方法
2017/12/31 Python
python Django中models进行模糊查询的示例
2019/07/18 Python
Python猴子补丁知识点总结
2020/01/05 Python
Numpy 理解ndarray对象的示例代码
2020/04/03 Python
python操作yaml说明
2020/04/08 Python
浅谈keras中自定义二分类任务评价指标metrics的方法以及代码
2020/06/11 Python
Django CBV模型源码运行流程详解
2020/08/17 Python
Python:__eq__和__str__函数的使用示例
2020/09/26 Python
C,C++的几个面试题小集
2013/07/13 面试题
国际商务系学生个人的自我评价
2013/11/26 职场文书
采购主管工作职责
2013/12/12 职场文书
洗煤厂厂长岗位职责
2014/01/03 职场文书
临床护士自荐信
2014/01/31 职场文书
解除劳动合同协议书范本
2014/04/14 职场文书
2015社区个人工作总结范文
2015/05/13 职场文书
党员干部学习心得体会
2016/01/23 职场文书
Golang 使用Map实现去重与set的功能操作
2021/04/29 Golang
如何设计高效合理的MySQL查询语句
2021/05/26 MySQL
pytorch Dropout过拟合的操作
2021/05/27 Python
Nginx虚拟主机的配置步骤过程全解
2022/03/31 Servers
Axios代理配置及封装响应拦截处理方式
2022/04/07 Vue.js