jQuery数据缓存功能的实现思路及简单模拟


Posted in Javascript onMay 27, 2013

前言
对于jQuery的数据缓存,相信大家都不会陌生,jQuery缓存系统不仅运用于DOM元素,动画、事件等都有用到这个缓存系统。所以在平时实际应用中, 我们经常需要给元素缓存一些数据,并且这些数据往往和DOM元素紧密相关。由于DOM元素(节点)也是对象, 所以我们可以直接扩展DOM元素的属性,但是如果给DOM元素添加自定义的属性和过多的数据可能会引起内存泄漏,所以应该要尽量避免这样做。 因此更好的解决方法是使用一种低耦合的方式让DOM和缓存数据能够联系起来。

另外:对于jQuery.data和jQuery.removeData静态方法、以及基于这两个方法的原型扩展方法的介绍和用法就不多说了,可以查看官方API文档。

实现思路
jQuery提供了一套灵活和强大的缓存方法:
(1)先在jQuery内部创建一个cache对象{}, 来保存缓存数据。 然后往需要进行缓存的DOM节点上扩展一个值为expando的属性, 这里是”jQuery” + (new Date).getTime()。 注:expando的值等于”jQuery”+当前时间, 元素本身具有这种属性的可能性很少,所以可以忽略冲突。
(2)接着把每个节点的dom[expando]的值都设为一个自增的变量id,保持全局唯一性。 这个id的值就作为cache的key用来关联DOM节点和数据。也就是说cache[id]就取到了这个节点上的所有缓存,即id就好比是打开一个房间(DOM节点)的钥匙。 而每个元素的所有缓存都被放到了一个map映射里面,这样可以同时缓存多个数据。
(3)所以cache对象结构应该像下面这样:

var cache = { 
"uuid1": { // DOM节点1缓存数据,"uuid1"相当于dom1[expando] 
"name1": value1, 
"name2": value2 
}, 
"uuid2": { // DOM节点2缓存数据,“uuid2"相当于dom2[expando] 
"name1": value1, 
"name2": value2 
} 
// ...... 
};

每个uuid对应一个elem缓存数据,每个缓存对象是可以由多个name/value(名值对)对组成的,而value是可以是任何数据类型的。

简单模拟实现
根据以上思路,就可以简单实现下jQuery.data和jQuery.removeDate的功能了:

(function(window, undefined) { 
var cacheData = {}, // 用来存储数据的对象 
win = window, // 把window缓存给一个变量 
uuid = 0, 
// 声明随机数(8位) 
// 注意+new Date()生成的随机数是Number类型,与一个空字符串连接后(或使用toString方法转型后)变成字符串,才可使用slice方法。 
expando = "cacheData" + (+new Date() + "").slice(-8); 
// (+new Date()).toString().slice(-8)等价于expando 
// 写入缓存 
var data = function(elem, name, value) { 
// 或使用原生方法验证字符串Object.prototype.toString.call(elem) === "[object String]" 
// 如果elem为字符串 
if (typeof elem === "string") { 
// 如果传入name参数,则为写入缓存 
if (name !== undefined) { 
cacheData[elem] = name; 
} 
// 返回缓存数据 
return cacheData[elem]; 
// 如果elem为DOM节点 
} else if (typeof elem === "object") { 
var id, 
thisCache; 
// 如果elem不存在expando属性,则添加一个expando属性(第一次给元素设置缓存),否则直接获取已有的expando和id值 
if (!elem[expando]) { 
id = elem[expando] = ++uuid; 
thisCache = cacheData[id] = {}; 
} else { 
id = elem[expando]; 
thisCache = cacheData[id]; 
} 
// 把一个随机数作为当前缓存对象的一个属性,利用该随机数就能找到该缓存对象 
if (!thisCache[expando]) { 
thisCache[expando] = {}; 
} 
if (value !== undefined) { 
// 将数据存到缓存对象中 
thisCache[expando][name] = value; 
} 
// 返回DOM元素存储的数据 
return thisCache[expando][name]; 
} 
}; 
// 删除缓存 
var removeData = function(elem, name) { 
// 如果elem为字符串,则直接删除该属性值 
if (typeof elem === "string") { 
delete cacheData[elem]; 
// 如果key为DOM节点 
} else if (typeof elem === "object") { 
// 如果elem不存在expando属性,则终止执行,不用删除缓存 
if (!elem[expando]) { 
return; 
} 
// 检测对象是否为空 
var isEmptyObject = function(obj) { 
var name; 
for (name in obj) { 
return false; 
} 
return true; 
} 
removeAttr = function() { 
try { 
// IE8即标准浏览器可以直接使用delete来删除属性 
delete elem[expando]; 
} catch (e) { 
// IE6/IE7使用removeAttribute方法来删除属性 
elem.removeAttribute(expando); 
} 
}, 
id = elem[expando]; 
if (name) { 
// 只删除指定的数据 
delete cacheData[id][expando][name]; 
// 如果是空对象,id所对应的数据对象全部删除 
if (isEmptyObject(cacheData[id][expando])) { 
delete cacheData[id]; 
removeAttr(); 
} 
} else { 
// 删除DOM元素存到缓存中的所有数据 
delete cacheData[id]; 
removeAttr(); 
} 
} 
}; 
// 把data和removeData挂在window全局对象下,这样在外部也能访问到这两个函数 
win.expando = expando; 
win.data = data; 
win.removeData = removeData; 
})(window, undefined);

例子:
HTML结构:
<div id="demo" style="height: 100px; width: 100px; background: #ccc; color: #fff; margin: 20px; text-align: center; line-height: 100px;"> 
demo 
</div>

js代码:
window.onload = function() { 
// 测试 
var demo = document.getElementById("demo"); 
// 写入缓存 
data(demo, "myName", "hcy"); 
console.log(data(demo, "myName")); // hcy 
data(demo, "myBlog", "http://www.cnblogs.com/cyStyle"); 
console.log(data(demo, "myBlog")); // http://www.cnblogs.com/cyStyle 
// 删除DOM元素的某个缓存值 
removeData(demo, "myBlog"); 
console.log(data(demo, "myBlog")); // undefined 
console.log(data(demo, "myName")); // hcy 
console.log(demo[expando]); // 1 
// 删除DOM元素 
removeData(demo); 
console.log(demo[expando]); // undefined 
};

firefox下例子结果截图:
jQuery数据缓存功能的实现思路及简单模拟 
对于上述例子实现jQuery的简单缓存系统:先给该DOM元素添加一个随机生成的属性expando,这个属性用来存放访问缓存数据的id值,就好比DOM元素都有一把开启缓存保险箱的钥匙,只要有了钥匙就可以随时开启缓存保险箱。 将本来存放到DOM元素中的数据都转到了缓存中,而DOM元素本身只要存储一个简单的属性就可以了,这样就可以将由DOM元素引起的内存泄漏(具体会发生什么状况不知道,大家都这么说~)的风险规避到最小。

结语
糊里糊涂地又到了最后,有一些术语或解释上可能存在偏差,望各位童鞋指正和给出一些建议;另外,从理论上讲, data和removeData方法可以用于任何对象的缓存, 不过如果运用于本地对象或window对象, 会存在内存泄露、循环引用等问题(^_^从网上看到的), 所以一般还是用于DOM节点比较适合,还可以结合事件、动画对DOM节点进行缓存数据的操作。ps:cache真的很重要!需要慢慢体会~
因为分享,所以简单;因为分享,所以快乐。

Javascript 相关文章推荐
究竟什么是Node.js?Node.js有什么好处?
May 29 Javascript
创建你的第一个AngularJS应用的方法
Jun 16 Javascript
JavaScript实现跨浏览器的添加及删除事件绑定函数实例
Aug 04 Javascript
利用jquery制作滚动到指定位置触发动画
Mar 26 Javascript
js事件处理程序跨浏览器解决方案
Mar 27 Javascript
ES6新特性五:Set与Map的数据结构实例分析
Apr 21 Javascript
纯js实现的积木(div层)拖动功能示例
Jul 19 Javascript
JavaScript获取tr td 的三种方式全面总结(推荐)
Aug 15 Javascript
基于滚动条位置判断的简单实例
Dec 14 Javascript
vue项目实现github在线预览功能
Jun 20 Javascript
vue实现弹框遮罩点击其他区域弹框关闭及v-if与v-show的区别介绍
Sep 29 Javascript
在微信小程序中使用图表的方法示例
Apr 25 Javascript
jQuery函数的等价原生函数代码示例
May 27 #Javascript
JS HTML5 音乐天气播放器(Ajax获取天气信息)
May 26 #Javascript
jQuery动态地获取系统时间实现代码
May 24 #Javascript
JavaScript事件处理器中的event参数使用介绍
May 24 #Javascript
Jquery多选下拉列表插件jquery multiselect功能介绍及使用
May 24 #Javascript
js过滤HTML标签以及空格的思路及代码
May 24 #Javascript
jQuery实现表头固定效果的实例代码
May 24 #Javascript
You might like
PHP实现HTML页面静态化的方法
2015/11/04 PHP
PHP 微信支付类 demo
2015/11/30 PHP
thinkPHP5.1框架路由::get、post请求简单用法示例
2019/05/06 PHP
JS修改css样式style浅谈
2013/05/06 Javascript
Highcharts 非常实用的Javascript统计图demo示例
2013/07/03 Javascript
js实现瀑布流的一种简单方法实例分享
2013/11/04 Javascript
浅析JavaScript中的常用算法与函数
2013/11/21 Javascript
深入理解javascript原型链和继承
2014/09/23 Javascript
JS判断浏览器是否支持某一个CSS3属性的方法
2014/10/17 Javascript
jquery实现公告翻滚效果
2015/02/27 Javascript
基于jquery实现页面滚动时顶部导航显示隐藏
2020/04/20 Javascript
Bootstrap每天必学之媒体对象
2015/11/30 Javascript
js表单处理中单选、多选、选择框值的获取及表单的序列化
2016/03/08 Javascript
基于jQuery的select下拉框选择触发事件实例分析
2016/11/18 Javascript
详解Js模板引擎(TrimPath)
2016/11/22 Javascript
python爬取安居客二手房网站数据(实例讲解)
2017/10/19 Javascript
html中通过JS获取JSON数据并加载的方法
2017/11/30 Javascript
Vue组件内部实现一个双向数据绑定的实例代码
2019/04/04 Javascript
jQuery删除/清空指定元素的所有子节点实例代码
2019/07/04 jQuery
Vue-cli3.X使用px2 rem遇到的问题及解决方法
2019/08/08 Javascript
Layui tree 下拉菜单树的实例代码
2019/09/21 Javascript
vuejs element table 表格添加行,修改,单独删除行,批量删除行操作
2020/07/18 Javascript
JS数组转字符串实现方法解析
2020/09/04 Javascript
[02:02]2018DOTA2亚洲邀请赛Mineski赛前采访
2018/04/04 DOTA
零基础写python爬虫之HTTP异常处理
2014/11/05 Python
python通过post提交数据的方法
2015/05/06 Python
python多线程+代理池爬取天天基金网、股票数据过程解析
2019/08/13 Python
阿里巴巴英国:Alibaba英国
2019/12/11 全球购物
linux面试题参考答案(6)
2014/08/29 面试题
黄继光的英雄事迹材料
2014/02/13 职场文书
信息服务专业毕业生求职信
2014/03/02 职场文书
在职党员进社区活动总结
2014/07/05 职场文书
裁员通知
2015/04/25 职场文书
入团介绍人意见范文
2015/06/04 职场文书
共青团优秀团员申请书(范文)
2019/08/15 职场文书
小学生作文之《压岁钱的烦恼》
2019/09/27 职场文书