jQuery源码分析系列:数据缓存
2013-04-23 15:28
417 查看
很多同学在项目中都喜欢将数据存储在HTMLElement属性上,如
给页面中div添加了自定义属性“data”及值“some data”。后续JS代码中使用getAttribute获取。
jQuery从1.2.3开始提供了data/removeData方法用来存储/删除数据。1.6.1代码片段
即给jQuery添加了静态字段/方法,有jQuery.cache/jQuery.uuid/jQuery.expando等。下面分别介绍
jQuery.cache 空对象,用来缓存。它的结构较复杂。
jQuery.uuid 自增唯一的数字。
jQuery.expando
字符串,使用Math.random生成,去掉了非数字字符。它作为HTMLElement或JS对象的属性名。
jQuery.noData
JS对象,对于指定的HTMLElement禁用data方法。如embed、applet。
jQuery.hasData
用来判断HTMLElement或JS对象是否具有数据。返回true或false。即如果调用了jQuery.data方法添加了属性,则返回true。
jQuery.acceptData 用来判断该元素是否能接受数据,返回true或false。在jQuery.data中使用。
jQuery.data 这是提供给客户端程序员使用的方法,它同时是setter/getter。
1,传一个参数,返回附加在指定元素的所有数据,即thisCachejQuery.data(el);
// thisCache
2,传二个参数,返回指定的属性值jQuery.data(el, 'name');
3,传三个参数,设置属性及属性值jQuery.data(el, 'name', 'jack');jQuery.data(el, 'uu',
{});
4,传四个参数,第四个参数pvt仅提供给jQuery库自身使用。即jQuery._data方法中传true。因为jQuery的事件模块严重依赖于jQuery.data,为避免人为的不小心重写在这个版本中加入的。
jQuery.removeData
删除数据。
上面是jQuery数据缓存模块的整体概述,下面详细说下jQuery.data方法。jQuery.data为两种对象提供缓存:JS对象和HTMLElement
内部实现上也是有区别的,
1,为JS对象提供缓存时,直接将数据保存在JS对象上。cache为JS对象。此时会偷偷的给JS对象添加个属性(类似于jQuery16101803968874529044),属性值也是个JS对象。举例说明
myObj的结构如下
“jQuery16101803968874529044”这个字符串在data内部命名为id(注意并非HTMLElement元素的id),它实际就是jQuery.expando。上面已经提到它是在jQuery.js引入到页面后随机生成的。
2,为HTMLElement提供缓存时,却不会直接保存在HTMLElement上。而是保存在jQuery.cache上。cache为jQuery.cache。此时先给HTMLElement添加属性(类似于jQuery16101803968874529044),属性值为数字(1,2,3递增)。即只将一些数字保存在了HTMLElement上,不会直接将数据置入。这是因为IE老版本中可能会存在内存泄露危险。而HTMLElement如何与jQuery.cache建立联系呢? 还是id。刚刚提到属性值数字就是id。举例说明
el 上添加了属性jQuery.expando,值为id,这个id是从1开始递增的。而id又作为jQuery.cache的属性(key)。这样就HTMLElement就与jQuery.cache建立了联系。如图
不知注意到没有,jQuery.data还有第四个参数pvt,这个参数只在jQuery._data中使用。
jQuery._data从命名上就指定它是私有的,使用jQuery的客户端程序员不应该去调用该方法。jQuery的API文档上也不会公开它。
jQuery的数据缓存模块从1.2.3到1.6.1几乎每个版本都在变。jQuery._data的提出就是为了避免客户端程序员覆盖/重写了默写模块。如jQuery事件模块中事件handler等就使用jQuery.data存储,如果重写了该模块。那么事件模块将瘫痪。因此特意添加了pvt参数及jQuery._data方法。
但如果你刻意要破坏,那么还是可以做的。如下
整个jQuery.data设置(set)数据缓存的过程就是如此,理解的这个。取数据(get)的过程就好理解了。不重复。
用jQuery.extend方法扩展工具函数,jQuery.fn.extend调用jQuery.extend中扩展的方法缓存数据。
数据缓存源码:
<div data="some data">Test</div> <script> div.getAttribute('data'); // some data </script>
给页面中div添加了自定义属性“data”及值“some data”。后续JS代码中使用getAttribute获取。
jQuery从1.2.3开始提供了data/removeData方法用来存储/删除数据。1.6.1代码片段
jQuery.extend({ cache: {}, // Please use with caution uuid: 0, ... });
即给jQuery添加了静态字段/方法,有jQuery.cache/jQuery.uuid/jQuery.expando等。下面分别介绍
jQuery.cache 空对象,用来缓存。它的结构较复杂。
jQuery.uuid 自增唯一的数字。
jQuery.expando
字符串,使用Math.random生成,去掉了非数字字符。它作为HTMLElement或JS对象的属性名。
expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
jQuery.noData
JS对象,对于指定的HTMLElement禁用data方法。如embed、applet。
jQuery.hasData
用来判断HTMLElement或JS对象是否具有数据。返回true或false。即如果调用了jQuery.data方法添加了属性,则返回true。
<div>aa</div> <script> var div = document.getElementsByTagName('div')[0]; $.hasData(div); // false $.data(div, 'name','jack'); $.hasData(div); // true </script>
jQuery.acceptData 用来判断该元素是否能接受数据,返回true或false。在jQuery.data中使用。
jQuery.data 这是提供给客户端程序员使用的方法,它同时是setter/getter。
1,传一个参数,返回附加在指定元素的所有数据,即thisCachejQuery.data(el);
// thisCache
2,传二个参数,返回指定的属性值jQuery.data(el, 'name');
3,传三个参数,设置属性及属性值jQuery.data(el, 'name', 'jack');jQuery.data(el, 'uu',
{});
4,传四个参数,第四个参数pvt仅提供给jQuery库自身使用。即jQuery._data方法中传true。因为jQuery的事件模块严重依赖于jQuery.data,为避免人为的不小心重写在这个版本中加入的。
jQuery.removeData
删除数据。
上面是jQuery数据缓存模块的整体概述,下面详细说下jQuery.data方法。jQuery.data为两种对象提供缓存:JS对象和HTMLElement
// 为JS对象提供缓存 var myObj = {}; $.data(myObj, 'name', 'jack'); $.data(myObj, 'name'); // jack // 为HTMLElement提供缓存 <div id="xx"></div> <script> var el = document.getElementById('xx'); $.data(el, 'name', 'jack'); $.data(el, 'name'); // jack </script>
内部实现上也是有区别的,
1,为JS对象提供缓存时,直接将数据保存在JS对象上。cache为JS对象。此时会偷偷的给JS对象添加个属性(类似于jQuery16101803968874529044),属性值也是个JS对象。举例说明
var myObj = {}; $.data(myObj, 'name', 'jack'); console.log(myObj);
myObj的结构如下
myObj = { jQuery16101803968874529044 : { name : 'jack' } }
“jQuery16101803968874529044”这个字符串在data内部命名为id(注意并非HTMLElement元素的id),它实际就是jQuery.expando。上面已经提到它是在jQuery.js引入到页面后随机生成的。
2,为HTMLElement提供缓存时,却不会直接保存在HTMLElement上。而是保存在jQuery.cache上。cache为jQuery.cache。此时先给HTMLElement添加属性(类似于jQuery16101803968874529044),属性值为数字(1,2,3递增)。即只将一些数字保存在了HTMLElement上,不会直接将数据置入。这是因为IE老版本中可能会存在内存泄露危险。而HTMLElement如何与jQuery.cache建立联系呢? 还是id。刚刚提到属性值数字就是id。举例说明
<div id="xx"></div> <script> var el = document.getElementById('xx'); $.data(el, 'name', 'jack'); console.log(el[jQuery.expando]); // 1 console.log(jQuery.cache); // {1 : {name:'jack'}} </script>
el 上添加了属性jQuery.expando,值为id,这个id是从1开始递增的。而id又作为jQuery.cache的属性(key)。这样就HTMLElement就与jQuery.cache建立了联系。如图
不知注意到没有,jQuery.data还有第四个参数pvt,这个参数只在jQuery._data中使用。
// For internal use only. _data: function( elem, name, data ) { return jQuery.data( elem, name, data, true ); },
jQuery._data从命名上就指定它是私有的,使用jQuery的客户端程序员不应该去调用该方法。jQuery的API文档上也不会公开它。
jQuery的数据缓存模块从1.2.3到1.6.1几乎每个版本都在变。jQuery._data的提出就是为了避免客户端程序员覆盖/重写了默写模块。如jQuery事件模块中事件handler等就使用jQuery.data存储,如果重写了该模块。那么事件模块将瘫痪。因此特意添加了pvt参数及jQuery._data方法。
但如果你刻意要破坏,那么还是可以做的。如下
<div id="xx">Test</div> <script> $('#xx').click(function(){ alert('click'); }); // 语句1 $.data($('#xx')[0], 'events', '', true); // 语句2 //$._data($('#xx')[0], 'events', ''); </script>
整个jQuery.data设置(set)数据缓存的过程就是如此,理解的这个。取数据(get)的过程就好理解了。不重复。
用jQuery.extend方法扩展工具函数,jQuery.fn.extend调用jQuery.extend中扩展的方法缓存数据。
数据缓存源码:
/* Start Data cache*/ var rbrace = /^(?:\{.*\}|\[.*\])$/,//花括号 rmultiDash = /([a-z])([A-Z])/g;//驼峰写法,大小写之间会被插入破折号 /**写入*/ //在匹配的DOM元素(elem)上附加一个唯一ID,在$.cache中添加同样的ID属性,该ID属性的值是一个对象,其中存储了key/value的映射 //.data(key,value)用来设置保存数据 .data(key)用来查询数据 jQuery.extend({ //数据存储在$.cache中,关键实现的核心 cache: {}, //整型值,初始为0 调用data接口自动加一 生成新的唯一ID 分配ID用的seed uuid: 0, /* 唯一ID附加在$.expando命名的属性上,$.expando是动态生成的,避免命名冲突 移除非数字编号的 为了区分不同的jQuery实例存储的数据,前缀 + 版本号 + 随机数作为 key*/ expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), //以下元素没有data 除了Flash之外的object noData: { "embed": true,//用于播放一个多媒体对象 ,flash 音频 视频 "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",//用于向页面添加多媒体对象 flash 音频 视频 "applet": true//object用于IE浏览器 embed元素用于非IE浏览器 }, //判断一个元素事发后有与之关联的数据(通过jQuery.data设置) 用在事件处理中 hasData: function( elem ) { //如果是DOM,就从jQuery.cache中读取,关联的jQuery.cache和DOM元素的id存储在jQueyr.expando中 //如果非DOM 直接从elem上取,jQuery.expando属性中有数据 //elem的属性jQuery.expando 要么值是id 要么存储数据 elem = elem.nodeType ? jQuery.cache[elem[jQuery.expando]]:elem[jQuery.expando]; return !!elem && !isEmptyDataObject(elem); }, /* 工具函数: jQuery.data(elem,key,value) 在指定元素上存储、添加任意数据 处理了循环引用和内存泄露问题 jQuery.data(elem,key) 返回指定元素上name指定的值 jQuery.data(elem) 返回全部数据 pvt私有的 是否是内部使用的独立对象 pvt为true用于事件处理 myObj = { js缓存直接绑定在js对象上 jQuery16101803968874529044 : { name : 'jack' } } 示例: <div id="xx"></div> <script> html是放在jquery.cache上面的 var el = document.getElementById('xx'); $.data(el, 'name', 'jack'); console.log(el[jQuery.expando]); // 1 console.log(jQuery.cache); // { 1 : { name:'jack'}} </script> HTMLElement -> jQuery.expando ->ID ->jQuery.cache */ /* data部分的代码明确区分了JS对象 和 DOM对象的保存,为了解决部分浏览器的内存泄露问题。当DOM和JS对象之间出现循环引用时,GC就无法正确处理。 */ data: function( elem, name, data, pvt /* Internal Use Only */ ) { //如果属于noData中定义的元素,是否可以附加数据,不可以直接返回 if(!jQuery.acceptData(elem)){ return; } var internalKey = jQuery.expando,//?? getByName = typeof name === "string", thisCache, //区分处理DOM元素和JS对象 IE不能垃圾回收对象跨DOM和JS 因为IE6-7不能垃圾回收对象跨DOM对象和JS对象进行的引用属性 isNode = elem.nodeType, //如果是DOM 使用全局jQuery.cache //如果是JS对象,直接附加在对象上 cache = isNode ? jQuery.cache : elem, //如果JS对象的cache已经存在,需要为JS对象定义一个ID //如果DOM元素,直接取elem[jQuery.expando] 返回id 为递增的 //如果是JS对象,且JS对象的属性jQuery.expando存在,返回jQuery.expando //var internalKey = jQuery.expando id = isNode ? elem[jQuery.expando] : elem[jQuery.expando] && jQuery.expando; //id不存在 id存在但是internalKey=jQuery.expando不存在 //data未定义,说明当前调用是查询数据,但是对象没有任何数据 直接返回 if((!id || (pvt && id && !cache[id][internalKey])) && getByName && data === undefined){ return; } //HTMLElement -> jQuery.expando -> id -> jQuery.cache //id不存在生成一个 设置id if(!id){ if(isNode){ //HTMLElement添加属性(类似于jQuery16101803968874529044),属性值为数字 1 2 3 4 //用uuid递增分配唯一ID,只有DOM元素需要。需要存在全局cache中 elem[jQuery.expando] = id = ++ jQuery.uuid; }else{ //避免与其他属性冲突 “jQuery16101803968874529044”这个字符串在data内部命名为id id = jQuery.expando; } } //数据存储在一个映射对象中 清空原有的值 if(!cache[id]){ cache[id] = {};//初始化存储对象 if(!isNode){ cache[id].toJSON = jQuery.noop; } } //用extend扩展cache,增加一个属性,用来保存数据。 //data接口接受对象和函数 浅拷贝 if(typeof name === "object" || typeof name === "function"){ if(pvt){ //id: 1 2 3 4 5 ... cache[id][internalKey] = jQuery.extend(cache[id][internalKey],name); }else{ cache[id] = jQuery.extend(cache[id],name); } } //存储了所有的数据的映射对象 thisCache = cache[id]; //避免key冲突 if ( pvt ) { if ( !thisCache[ internalKey ] ) { thisCache[ internalKey ] = {};//设空对象 } thisCache = thisCache[ internalKey ]; } if ( data !== undefined ) { thisCache[ jQuery.camelCase( name ) ] = data; } //忽略 if ( name === "events" && !thisCache[name] ) { return thisCache[ internalKey ] && thisCache[ internalKey ].events; } //如果name是字符串,返回data 不是 返回整个存储对象 return getByName ? thisCache[ jQuery.camelCase( name ) ] : thisCache; }, //删除数据 removeData: function( elem, name, pvt /* Internal Use Only */ ) { //如果元素不能附加数据 if ( !jQuery.acceptData( elem ) ) { return; } //internalKey定义唯一ID var internalKey = jQuery.expando, //是DOM对象 isNode = elem.nodeType, //DOM对象用cache全局缓存 JS对象存在elem中 cache = isNode ? jQuery.cache : elem, // 如果elem的jQuery.expando已经有值了,就重用 减少ID数 id = isNode ? elem[ jQuery.expando ] : jQuery.expando; //缓存中没有这个元素 直接返回 if ( !cache[ id ] ) { return; } //部分浏览器不支持deleteExpando ,在jQuery.support中检查过这个浏览器特性 失败的话 设null if(jQuery.support.deleteExpando || cache != window){ delete cache[id]; }else{ cache[id] = null; } var internalCache = cache[id][internalKey]; //如果还有数据 就清空一次在设置 增加性能 if(internalCache){ cache[id] = {}; cache[id][internalKey] = internalCache; //没有任何数据了 }else if(isNode){ //如果支持delete 就删除 IE使用reomveAttribute if(jQuery.support.deleteExpando){ delete elem[jQuery.expando]; }else if(elem.reomveAttribute){ elem.reomveAttribute(jQuery.expando); }else{ elem[jQuery.expando] = null; } } }, _data: function( elem, name, data ) { return jQuery.data(elem,name,data,true); }, //判断一个元素是否可以附加数据 acceptData: function( elem ) { if(elem.nodeName){ //embed object applet 以下元素没有data var match = jQuery.noData[elem.nodeName.toLowerCase()]; if(match){ //getAttribute():查询属性的名字 返回false return !(match === true || elem.getAttribute("classid") !== match); } } return true; } }); jQuery.fn.extend({ //向被选元素添加数据 和读取数据 data: function( key, value ) { var data = null;//初始化 //用于处理特殊key key是undefined 则认为取当前jQuery对象中第一个元素的全部数据 if(typeof key === "undefined"){ if(this.length){ data = jQuery.data(this[0]); //Element if(this[0].nodeType === 1){ var attr = this[0].attributes,name; 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]); } } } return data; //key是对象 则对当前jQuery对象迭代调用$.fn.each //在每一个匹配的元素上存储数据key }else if(typeof key === "object" ){ return this.each(function(){ jQuery.data(this,key); } } // 到这里,key是字符串 var parts = key.split("."); parts[1] = parts[1] ? "." + parts[1] : ""; //如果value为undefined,则 取当前jQuery对象中第一个元素指定名称的数据 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 ); // 读取HTML5的data属性 data = dataAttr( this[0], key, data ); } return data === undefined && parts[1] ? this.data( parts[0] ) : data; // key是字符串,value不是undefined,则存储 } else { return this.each(function() { var $this = jQuery( this ), args = [ parts[0], value ]; // 触发事件 $this.triggerHandler( "setData" + parts[1] + "!", args ); jQuery.data( this, key, value ); $this.triggerHandler( "changeData" + parts[1] + "!", args ); }); } }, //删除数据 removeData: function( key ) { return this.each(function() { jQuery.removeData( this, key ); }); } });
相关文章推荐
- jQuery1.6.1源码分析系列
- jQuery源码分析系列:AJAX状态码
- jQuery-1.9.1源码分析系列(五) 回调对象
- jQuery-1.9.1源码分析系列(十三) 位置大小操作
- jQuery-1.9.1源码分析系列(十) 事件系统——事件委托
- jQuery-1.9.1源码分析系列(十) 事件系统——事件包装
- 菜鸟读jQuery 2.0.3 源码分析系列(1)
- jQuery源码分析系列(版本1.9 - 1.10)
- 菜鸟读jQuery 2.0.3 源码分析系列(2)
- jQuery1.6.1源码分析系列
- jQuery-1.9.1源码分析系列(九) CSS操作
- jQuery-1.9.1源码分析系列(十) 事件系统——事件体系结构
- jQuery-1.9.1源码分析系列(六) 延时对象
- jQuery-1.9.1源码分析系列(十四) 一些jQuery工具
- jQuery1.6.1源码分析系列(作者:nuysoft/高云)
- jQuery1.6.1源码分析系列(持续更新)
- Jquery-1.9.1源码分析系列(十一)之DOM操作
- jQuery源码分析系列:队列操作
- jquery进阶,源码分析系列
- jQuery源码分析系列:.domManip() .buildFragment() .clean()