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 相关文章推荐
JQuery 插件模板 制作jquery插件的朋友可以参考下
Mar 17 Javascript
Jquery中LigerUi的弹出编辑框(实现方法)
Jul 09 Javascript
javascript自动改变文字大小和颜色的效果的小例子
Aug 02 Javascript
jquery 实现窗口的最大化不论什么情况
Sep 03 Javascript
JavaScript实现向OL列表内动态添加LI元素的方法
Mar 21 Javascript
jQuery实现可移动选项的左右下拉列表示例
Dec 26 Javascript
AngularJS实现的回到顶部指令功能实例
May 17 Javascript
vue 实现复制内容到粘贴板clipboard的方法
Mar 17 Javascript
大转盘抽奖小程序版 转盘抽奖网页版
Apr 16 Javascript
Vue.js组件props数据验证实现详解
Oct 19 Javascript
微信小程序动态评分展示/五角星展示/半颗星展示/自定义长度展示功能的实现
Jul 22 Javascript
js实现特别简单的钟表效果
Sep 14 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 Switch 语句之学习笔记
2013/09/21 PHP
Apache连接PHP后无法启动问题解决思路
2015/06/18 PHP
PHP设置头信息及取得返回头信息的方法
2016/01/25 PHP
php微信开发接入
2016/08/27 PHP
PHP框架自动加载类文件原理详解
2017/06/06 PHP
php命令行写shell实例详解
2018/07/19 PHP
PHP curl批处理及多请求并发实现方法分析
2018/08/15 PHP
用javascript动态调整iframe高度的代码
2007/04/10 Javascript
文本框获得焦点和失去焦点的判断代码
2012/03/18 Javascript
使用jQuery时Form表单元素ID和name命名大忌
2014/03/06 Javascript
JS获取下拉框显示值和判断单选按钮的方法
2015/07/09 Javascript
jquery合并表格中相同文本的相邻单元格
2015/07/17 Javascript
jquery form表单获取内容以及绑定数据
2016/02/24 Javascript
jQuery动态加载css文件实现方法
2016/06/15 Javascript
webstorm和.vue中es6语法报错的解决方法
2018/05/08 Javascript
详解创建自定义的Angular Schematics
2018/06/06 Javascript
Auto.js自动收取自己和好友蚂蚁森林能量脚本
2018/06/28 Javascript
浅谈Vue组件及组件的注册方法
2018/08/24 Javascript
JS document form表单元素操作完整示例
2020/01/13 Javascript
Node.js web 应用如何封装到Docker容器中
2020/09/01 Javascript
利用python解决mysql视图导入导出依赖的问题
2017/12/17 Python
Python反转序列的方法实例分析
2018/03/21 Python
在python中利用opencv简单做图片比对的方法
2019/01/24 Python
pandas 如何分割字符的实现方法
2019/07/29 Python
python logging设置level失败的解决方法
2020/02/19 Python
python中Django文件上传方法详解
2020/08/05 Python
牵手50香港:专为黄金岁月的单身人士而设的交友网站
2020/08/14 全球购物
民事诉讼代理授权委托书
2014/10/11 职场文书
关于长城的导游词
2015/01/30 职场文书
医学生自荐信范文
2015/03/05 职场文书
今日说法观后感
2015/06/08 职场文书
运动会班级前导词
2015/07/20 职场文书
Nginx反向代理及负载均衡如何实现(基于linux)
2021/03/31 Servers
详解python字符串驻留技术
2021/05/21 Python
nginx中封禁ip和允许内网ip访问的实现示例
2022/03/17 Servers
python神经网络ResNet50模型
2022/05/06 Python