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 相关文章推荐
HTML 自动伸缩的表格Table js实现
Apr 01 Javascript
javascript常用方法、属性集合及NodeList 和 HTMLCollection 的浏览器差异
Dec 25 Javascript
Underscore.js 的模板功能介绍与应用
Dec 24 Javascript
JQuery工具函数汇总
Jun 15 Javascript
js实现的简单radio背景颜色选择器代码
Aug 18 Javascript
JS组件Bootstrap Table表格多行拖拽效果实现代码
Dec 08 Javascript
Javascript数组Array基础介绍
Mar 13 Javascript
Bootstrap组件(一)之菜单
May 11 Javascript
jQuery实现二维码扫描功能
Jan 09 Javascript
JS设计模式之单例模式(一)
Sep 29 Javascript
JS实现横向轮播图(初级版)
Jun 24 Javascript
vue-cli单页面预渲染seo-prerender-spa-plugin操作
Aug 10 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的FTP学习(二)[转自奥索]
2006/10/09 PHP
基于PHP对XML的操作详解
2013/06/07 PHP
C/S和B/S两种架构区别与优缺点分析
2014/10/23 PHP
php生成rss类用法实例
2015/04/14 PHP
Yii框架组件和事件行为管理详解
2016/05/20 PHP
PHP实现随机生成水印图片功能
2017/03/22 PHP
JavaScript 调试器简介
2009/02/21 Javascript
AppBaseJs 类库 网上常用的javascript函数及其他js类库写的
2010/03/04 Javascript
浅谈javascript的数据类型检测
2010/07/10 Javascript
jQuery load方法用法集锦
2011/12/06 Javascript
js replace 与replaceall实例用法详解
2013/08/03 Javascript
js禁止页面使用右键(简单示例代码)
2013/11/13 Javascript
创建、调用JavaScript对象的方法集锦
2014/12/24 Javascript
js实现带关闭按钮始终显示在网页最底部工具条的方法
2015/03/02 Javascript
Javascript闭包(Closure)详解
2015/05/05 Javascript
KVM虚拟化技术之使用Qemu-kvm创建和管理虚拟机的方法
2016/10/05 Javascript
浅谈javascript控制HTML5的全屏操控,浏览器兼容的问题
2016/10/10 Javascript
vue使用element-ui的el-input监听不了回车事件的解决方法
2018/01/12 Javascript
代码详解javascript模块加载器
2018/03/04 Javascript
vue实现键盘输入支付密码功能
2018/08/18 Javascript
jQuery点击页面其他部分隐藏下拉菜单功能
2018/11/27 jQuery
javascript 高级语法之继承的基本使用方法示例
2019/11/11 Javascript
[16:27]DOTA2 HEROS教学视频教你分分钟做大人-艾欧
2014/06/11 DOTA
Python中itertools模块用法详解
2014/09/25 Python
Python中实现结构相似的函数调用方法
2015/03/10 Python
python+pyqt5实现图片批量缩放工具
2019/03/18 Python
python二维键值数组生成转json的例子
2019/12/06 Python
详解python爬取弹幕与数据分析
2020/11/14 Python
python-图片流传输的思路及示例(url转换二维码)
2020/12/21 Python
《海底世界》教学反思
2014/04/16 职场文书
车队司机个人自我鉴定
2014/04/17 职场文书
大学奖学金获奖感言
2014/08/15 职场文书
平遥古城导游词
2015/02/03 职场文书
工作表现证明
2015/06/15 职场文书
2016年社区服务活动总结
2016/04/06 职场文书
深入解析NumPy中的Broadcasting广播机制
2021/05/30 Python