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 相关文章推荐
关于URL中的特殊符号使用介绍
Nov 03 Javascript
定义JavaScript二维数组采用定义数组的数组来实现
Dec 09 Javascript
jquery隔行换色效果实现方法
Jan 15 Javascript
jquery实现实时改变网页字体大小、字体背景色和颜色的方法
Aug 05 Javascript
jQuery实现伪分页的方法分享
Feb 17 Javascript
浅析jQuery Ajax请求参数和返回数据的处理
Feb 24 Javascript
JavaScript绑定事件监听函数的通用方法
May 14 Javascript
jQuery实现判断控件是否显示的方法
Jan 11 Javascript
Bootstrap面板(Panels)的简单实现代码
Mar 17 Javascript
微信小程序wx.navigateTo中events属性实现页面间通信传值,数据同步
Jul 13 Javascript
JavaScript实现抖音罗盘时钟
Oct 11 Javascript
VSCode写vue项目一键生成.vue模版,修改定义其他模板的方法
Apr 17 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
我的论坛源代码(三)
2006/10/09 PHP
PHP-Fcgi下PHP的执行时间设置方法
2013/08/02 PHP
JoshChen_php新手进阶高手不可或缺的规范介绍
2013/08/16 PHP
PHP读取CURL模拟登录时生成Cookie文件的方法
2014/11/04 PHP
php实现的读取CSV文件函数示例
2017/02/07 PHP
yii框架redis结合php实现秒杀效果(实例代码)
2017/10/26 PHP
记录Yii2框架开发微信公众号遇到的问题及解决方法
2018/07/20 PHP
PHP+jQuery实现双击修改table表格功能示例
2019/02/21 PHP
JavaScript基础知识之数据类型
2012/08/06 Javascript
Javascript拓展String方法小结
2013/07/08 Javascript
购物车选中得到价格实现示例
2014/01/26 Javascript
JavaScript函数的4种调用方法详解
2014/04/22 Javascript
js实现飞入星星特效代码
2014/10/17 Javascript
Javascript学习笔记之函数篇(五) : 构造函数
2014/11/23 Javascript
JavaScript中的逻辑判断符&amp;&amp;、||与!介绍
2014/12/31 Javascript
浅谈javascript基础之客户端事件驱动
2016/06/10 Javascript
javascript如何创建对象
2016/08/29 Javascript
AngularJS前端页面操作之用户修改密码功能示例
2017/03/27 Javascript
vue.js在标签属性中插入变量参数的方法
2018/03/06 Javascript
webpack实现一个行内样式px转vw的loader示例
2018/09/13 Javascript
Vue实现搜索结果高亮显示关键字
2019/05/28 Javascript
JavaScript实现无限轮播效果
2020/11/19 Javascript
[01:08:17]2018DOTA2亚洲邀请赛3月29日 小组赛B组 EG VS VGJ.T
2018/03/30 DOTA
实例讲解Python中函数的调用与定义
2016/03/14 Python
Anaconda2 5.2.0安装使用图文教程
2018/09/19 Python
python-opencv 将连续图片写成视频格式的方法
2019/01/08 Python
聊聊Python pandas 中loc函数的使用,及跟iloc的区别说明
2021/03/03 Python
小学家长会邀请函
2014/01/23 职场文书
《三亚落日》教学反思
2014/04/26 职场文书
公司应聘求职信
2014/06/21 职场文书
办理信用卡收入证明范例
2014/09/13 职场文书
公路局群众路线教育实践活动第一阶段工作汇报
2014/10/25 职场文书
2014年纪检监察工作总结
2014/11/11 职场文书
2016年清明节寄语
2015/12/04 职场文书
《文化苦旅》读后感:阅读,让人诗意地栖居在大地上
2019/12/24 职场文书
2021年最新用于图像处理的Python库总结
2021/06/15 Python