关于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 相关文章推荐
在JavaScript中遭遇级联表达式陷阱
Mar 08 Javascript
jQuery maxlength文本字数限制插件
Apr 16 Javascript
jquery 实现表单验证功能代码(简洁)
Jul 03 Javascript
JS实现数组去重复值的方法示例
Feb 18 Javascript
Bootstrap3 模态框使用实例
Feb 22 Javascript
基于vue2.0的活动倒计时组件countdown(附源码下载)
Oct 09 Javascript
详解javascript replace高级用法
Feb 17 Javascript
layui添加动态菜单与选项卡
Jul 26 Javascript
jquery实现的放大镜效果示例
Feb 24 jQuery
Selenium执行Javascript脚本参数及返回值过程详解
Apr 01 Javascript
js实现三角形粒子运动
Sep 22 Javascript
浅谈vant组件Picker 选择器选单选问题
Nov 04 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安全编程之加密功能
2006/10/09 PHP
用文本文件制作留言板提示(上)
2006/10/09 PHP
PHP可变函数的使用详解
2013/06/14 PHP
PHP实现获取某个月份周次信息的方法
2015/08/11 PHP
ThinkPHP的常用配置选项汇总
2016/03/24 PHP
cakephp常见知识点汇总
2017/02/24 PHP
php 调用百度sms来发送短信的实现示例
2018/11/02 PHP
PHP经典设计模式之依赖注入定义与用法详解
2019/05/21 PHP
javascript权威指南 学习笔记之null和undefined
2011/09/25 Javascript
JavaScript语言核心数据类型和变量使用介绍
2013/08/23 Javascript
jquery validate表单验证插件
2016/09/06 Javascript
input框中的name和id的区别
2016/11/16 Javascript
JavaScript函数基础详解
2017/02/03 Javascript
JS实现仿饿了么在浏览器标签页失去焦点时网页Title改变
2017/06/01 Javascript
iview table render集成switch开关的实例
2018/03/14 Javascript
JS数据类型STRING使用实例解析
2019/12/18 Javascript
JavaScript中clientWidth,offsetWidth,scrollWidth的区别
2021/01/25 Javascript
[46:49]完美世界DOTA2联赛PWL S3 access vs Rebirth 第二场 12.19
2020/12/24 DOTA
python实现DNS正向查询、反向查询的例子
2014/04/25 Python
Python ORM框架SQLAlchemy学习笔记之安装和简单查询实例
2014/06/10 Python
wxPython定时器wx.Timer简单应用实例
2015/06/03 Python
Zabbix实现微信报警功能
2016/10/09 Python
python select.select模块通信全过程解析
2017/09/20 Python
基于python socketserver框架全面解析
2017/09/21 Python
浅谈tensorflow中几个随机函数的用法
2018/07/27 Python
使用Python实现 学生学籍管理系统
2019/11/26 Python
Python属性和内建属性实例解析
2020/01/14 Python
Python要如何实现列表排序的几种方法
2020/02/21 Python
PyQt5 控件字体样式等设置的实现
2020/05/13 Python
获取CSDN文章内容并转换为markdown文本的python
2020/09/06 Python
彻底解决pip下载pytorch慢的问题方法
2021/03/01 Python
Sephora丝芙兰泰国官方网站:国际知名化妆品购物
2017/11/15 全球购物
公务员年总结的自我评价
2013/10/25 职场文书
库房管理员岗位职责
2014/03/09 职场文书
小学校长汇报材料
2014/08/20 职场文书
Python实现简单的俄罗斯方块游戏
2021/09/25 Python