关于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 相关文章推荐
jquery 插件开发 extjs中的extend用法小结
Jan 04 Javascript
JavaScript加强之自定义callback示例
Sep 21 Javascript
原生js实现改变随意改变div属性style的名称和值的结果
Sep 26 Javascript
javascript实现2048游戏示例
May 04 Javascript
jquery左边浮动到一定位置时显示返回顶部按钮
Jun 05 Javascript
理解javascript异步编程
Jan 27 Javascript
jQuery提示插件qTip2用法分析(支持ajax及多种样式)
Jun 08 Javascript
简单的JS轮播图代码
Jul 18 Javascript
使用Javascript监控前端相关数据的代码
Oct 27 Javascript
vue监听对象及对象属性问题
Aug 20 Javascript
javascript canvas API内容整理
Feb 16 Javascript
微信小程序APP的生命周期及页面的生命周期
Apr 19 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
通过缓存数据库结果提高PHP性能的原理介绍
2012/09/05 PHP
php实现读取超大文件的方法
2014/07/28 PHP
将PHP从5.3.28升级到5.3.29时Nginx出现502错误
2015/05/09 PHP
PHP去除字符串最后一个字符的三种方法实例
2017/03/01 PHP
如何修改yii2.0自带的user表为其它的表
2017/08/01 PHP
用javascript实现计算两个日期的间隔天数
2007/08/14 Javascript
原生JS可拖动弹窗效果实例代码
2013/11/09 Javascript
js全屏显示显示代码的三种方法
2013/11/11 Javascript
js动态删除div元素基本思路及实现代码
2014/05/08 Javascript
window.returnValue使用方法示例介绍
2014/07/03 Javascript
Javascript window对象详解
2014/11/12 Javascript
jQuery+ajax简单实现文件上传的方法
2016/06/03 Javascript
prototype.js常用函数详解
2016/06/18 Javascript
jquery鼠标悬停导航下划线滑出效果
2017/09/29 jQuery
基于vue-ssr的静态网站生成器VuePress 初体验
2018/04/17 Javascript
vue中el-upload上传图片到七牛的示例代码
2018/10/19 Javascript
Vue实现回到顶部和底部动画效果
2019/07/31 Javascript
layui 实现自动选择radio单选框(checked)的方法
2019/09/03 Javascript
原生js实现日期选择插件
2020/05/21 Javascript
vue 单页应用和多页应用的优劣
2020/10/22 Javascript
用python实现的去除win下文本文件头部BOM的代码
2013/02/10 Python
python解决网站的反爬虫策略总结
2016/10/26 Python
Python多进程与服务器并发原理及用法实例分析
2018/08/21 Python
Java ExcutorService优雅关闭方式解析
2020/05/30 Python
AmazeUI 等分网格的实现示例
2020/08/25 HTML / CSS
REISS英国官网:伦敦High Street最受欢迎品牌
2016/12/21 全球购物
百丽国际旗下购物网站:优购
2017/02/28 全球购物
无畏的旅行:Intrepid Travel
2017/12/20 全球购物
Linux文件系统类型
2012/09/16 面试题
培训协议书范本
2014/04/22 职场文书
优秀党务工作者事迹材料
2014/05/07 职场文书
留学经费担保书
2014/05/12 职场文书
《中国梦我的梦》中学生演讲稿
2014/08/20 职场文书
JS异步堆栈追踪之为什么await胜过Promise
2021/04/28 Javascript
redis 查看所有的key方式
2021/05/07 Redis