深入理解jQuery.data() 的实现方式


Posted in Javascript onNovember 30, 2016

jQuery.data() 的作用是为普通对象或 DOM Element 附加(及获取)数据。 

下面将分三个部分分析其实现方式: 

1. 用name和value为对象附加数据;即传入三个参数,第一个参数为需要附加数据的对象,第二个参数为数据的名称,第三个参数为数据的值。当然,只是获取值的话,也可以不传入第三个参数。 

2. 用另一个对象为对象附加数据;即传入两个参数,第一个参数为需要附加的数据对象(我们称之为“obj”),第二个参数也是一个对象(我们称之为“another”);“another”中包含的键值对将会被复制到 “obj” 的数据缓存(我们称之为“cache”)中。 

3. 为 DOM Element 附加数据;DOM Element 也是一种 Object ,但 IE6、IE7 对直接附加在 DOM Element 上的对象的垃圾回收存在问题;因此我们将这些数据存放在全局缓存(我们称之为“globalCache”)中,即 “globalCache” 包含了多个 DOM Element 的 “cache”,并在 DOM Element 上添加一个属性,存放 “cache” 对应的 uid 。

用name和value为对象附加数据

使用 jQuery.data() 为普通对象附加数据时,其本质是将一个 “cache” 附加到了对象上,并使用了一个特殊的属性名称。 

存放数据的 “cache” 也是一个 object,我们为 “obj” 附加的数据实际上成为了 “cache” 的属性。而 “cache” 又是 “obj” 的一个属性,在 jQuery 1.6中,这个属性的名称是 “jQuery16”加上一个随机数(如下面提到的 “jQuery16018518865841457738” )。

我们可以用下面的代码来测试 jQuery.data() 的功能:

<script type="text/javascript" src="jqueryjs"></script> 
<script> 
obj = {}; 
$data(obj, 'name', 'value'); 
documentwrite("$data(obj, 'name') = " + $data(obj, 'name') + '<br />'); 
 
for (var key in obj) { 
 documentwrite("obj" + key + 'name = ' + obj[key]name); 
} 
</script>

显示结果为:

$.data(obj, 'name') = value 
obj.jQuery16018518865841457738.name = value

在这段代码中,我们首先在 “obj” 上附加了一个属性(名称为“name”,值为“value”),然后通过 $.data(obj, 'name') 来获取所附加的数据。为了深入了解其中的实现机制,我们有使用了一个循环来获取 “obj” 的属性,实际上是取出了在 “obj” 上附加的 “cache”对象。

可以看到,jQuery.data() 实际上为 “obj” 附加到了名为 “jQuery16018518865841457738” (这个名称是随机的)的对象,也就是 “cache” 上。用 jquery.data() 方式为对象附加的属性实际上成为了这个 “cache” 的属性。

我们可以用下面的代码实现类似的功能:

$ = function() { 
 var expando = "jQuery" + ("6" + Mathrandom())replace(/\D/g, ''); 
 
 function getData(cache, name) { 
 return cache[name]; 
 } 
 
 function setData(cache, name, value) { 
 cache[name] = value; 
 } 
 
 function getCache(obj) { 
 obj[expando] = obj[expando] || {}; 
 return obj[expando]; 
 } 
 
 return { 
 data : function(obj, name, value) { 
  var cache = getCache(obj); 
 
  if (value === undefined) { 
  return getData(cache, name); 
  } else { 
  setData(cache, name, value); 
  } 
 } 
 } 
}();

function 中的第一行代码定义了 “expando” ,即 "jQuery1.6" 加上一个随机数(0.xxxx),并将其中非数字的部分去掉;这种格式将在jQuery的其他地方用到,这里不做探讨;只需要知道这是一个特殊的名称,并且可以用于标识不同的页面(比如不同 iframe 中的 “expando” 就会有所不同)。

接下来定义了获取数据的函数 getData(), 即从 “cache” 中获取一个属性;实际上也就是返回 cache[name] 。 

然后是 setData() 函数,用于设置 “cache” 的属性;实际上也就是设置 cache[name] 的值。 

之后是 getCache() , 获取 “obj” 上的 “cache”,即 obj[expando];如果 obj[expando] 为空,则进行初始化。 

最后公开了 data 方法,先根据传入的 “obj”,获取附加在 “obj” 上的 “cache”; 当传入两个参数时,调用 getData()方法;当传入三个参数时,则调用 setData() 方法。

用另一个对象为对象附加数据

除了以提供 name 和 value 的方式进行赋值,我们还可以直接传入另一个对象( “another” )作为参数。这种情况下,“another” 的属性名称和属性值将被视为多个键值对,从中提取的 “name” 和 “value” 都会被复制到目标对象的缓存中。

功能测试代码如下:

<script type="text/javascript" src="jqueryjs"></script> 
<script> 
obj = {}; 
$data(obj, {name1: 'value1', name2: 'value2'}); 
 
documentwrite("$data(obj, 'name1') = " + $data(obj, 'name1') + '<br />' ); 
documentwrite("$data(obj, 'name2') = " + $data(obj, 'name2') + '<br />'); 
 
for (var key in obj) { 
 documentwrite("obj" + key + 'name1 = ' + obj[key]name1 + '<br />'); 
 documentwrite("obj" + key + 'name2 = ' + obj[key]name2); 
} 
</script>

显示结果如下:

$.data(obj, 'name1') = value1 
$.data(obj, 'name2') = value2 
obj.jQuery1600233050178663064.name1 = value1 
obj.jQuery1600233050178663064.name2 = value2

上面的测试代码中,我们先将一个带有两个键值对的 “another” 对象传入,然后分别用 $.data(obj, 'name1') 和 $.data(obj, 'name2') 获取附加的数据;同样,为了深入了解其中的机制,我们通过遍历 “obj” 的方式取出了隐藏的 “cache” 对象,并获得了 “cache” 对象的 “name1” 属性和 “name2” 属性的值。

可以看到,jQuery.data() 实际上为 “obj” 附加了名为 “obj.jQuery1600233050178663064” 的对象,也就是 “cache” 上。用 jquery.data() 方式传入的键值对都被复制到了 “cache” 中。

我们可以用下面的代码实现类似的功能:

$ = function() { 
 // Other codes 
 
 function setDataWithObject(cache, another) { 
 for (var name in another) { 
  cache[name] = another[name]; 
 } 
 } 
 
 // Other codes 
 
 return { 
 data : function(obj, name, value) { 
  var cache = getCache(obj); 
 
  if (name instanceof Object) { 
  setDataWithObject(cache, name) 
  } else if (value === undefined) { 
  return getData(cache, name); 
  } else { 
  setData(cache, name, value); 
  } 
 } 
 } 
}();

这段代码是在之前的代码的基础上进行修改的。首先增加了内部函数 setDataWithObject() ,这个函数的实现是遍历 “another” 的属性,并复制到 “cache” 中。 

然后,在对外开放的 data 函数中,先判断传入的第二个参数的名称,如果这个参数是一个 Object 类型的实例,则调用 setDataWithObject() 方法。

为 DOM Element 附加数据

由于 DOM Element 也是一种 Object,因此之前的方式也可以为 DOM Element 赋值;但考虑到 IE6、IE7 中垃圾回收的问题(不能有效回收 DOM Element 上附加的对象引用),jQuery采用了与普通对象有所不同的方式附加数据。

测试代码如下:

<script type="text/javascript" src="datajs"></script> 
<script> 
windowonload = function() { 
 div = documentgetElementById('div_test'); 
 $data(div, 'name', 'value'); 
 documentwrite($data(div, 'name')); 
} 
</script>

显示结果如下:

value

测试代码中,首先通过 document.getElementById 方法获取了一个 DOM Element (当然,也可以用 jQuery 的选择器),然后在这个 DOM Element 上附加了一个属性,随后就从 DOM Element 上取出了附加的属性并输出。

因为考虑到 IE6、IE7 对 DOM Element 上的对象引用的垃圾回收存在问题,我们不会直接在 DOM Element 上附加对象;而是使用全局cache,并在 DOM Element 上附加一个 uid。

实现方式如下:

$ = function() { 
 var expando = "jQuery" + ("6" + Mathrandom())replace(/\D/g, ''); 
 var globalCache = {}; 
 var uuid = 0; 
 
 // Other codes 
 
 function getCache(obj) { 
 if (objnodeType) { 
  var id = obj[expando] = obj[expando] || ++uuid; 
  globalCache[id] = globalCache[id] || {}; 
  return globalCache[id]; 
 } else { 
  obj[expando] = obj[expando] || {}; 
  return obj[expando]; 
 } 
 } 
 
 // Other codes 
}();

 这段代码与之前的代码相比,增加了 globalCache 和 uuid,并修改了 getCache() 方法。

globalCache 对象用于存放附加到 DOM Element 上的 “cache”,可以视为 “cache” 的“容器”。uuid 表示 “cache” 对应的唯一标识,是唯一且自增长的。uuid 或被存放在 DOM Element 的 “expando” 属性中。 

getCache() 函数中增加了一个判断,即 “obj” 具有 “nodeType” 属性,就认为这是一个 DOM Element;这种情况下,就先取出附加在 “obj” 上的 id ,即 obj[expando] ;如果 obj[expando] 未定义,则先用 ++uuid 对其进行初始化;取出 id 之后,就到 globalCache 中找到对应的 “cache” ,即 globalCache[id], 并返回。

    到此为止,jQuery.data() 函数的实现就介绍完了;但是,这里还有一个需要思考的问题:为什不都统一用 “globalCache” 存储,而要将 “cache” 直接附加到普通对象上?我认为这应该是一种性能优化的方式,毕竟少一个引用的层次,存取速度应该会略快一些。 jQuery 中这刻意优化的地方非常多,在许多原本可以统一处理的对方都进行了特殊处理。但这在一定程度上,也造成了阅读源码的障碍。当然这是作者(及其他代码贡献者)本身的编程哲学,这里就不加评论了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
用js实现的一个Flash滚动轮换显示图片代码生成器
Mar 14 Javascript
读jQuery之十一 添加事件核心方法
Jul 31 Javascript
JS自定义对象实现Java中Map对象功能的方法
Jan 20 Javascript
浅析Javascript ES6新增值比较函数Object.is
Aug 24 Javascript
深入理解Webpack 中路径的配置
Jun 17 Javascript
关于在vue-cli中使用微信自动登录和分享的实例
Jun 22 Javascript
Angular2 自定义validators的实现方法
Jul 05 Javascript
认识less和webstrom的less配置方法
Aug 02 Javascript
使用Vue-cli 3.0搭建Vue项目的方法
Jun 07 Javascript
JavaScript封闭函数及常用内置对象示例
May 13 Javascript
jquery实现上传文件进度条
Mar 26 jQuery
ES11屡试不爽的新特性,你用上了几个
Oct 21 Javascript
利用vue-router实现二级菜单内容转换
Nov 30 #Javascript
JavaScript如何实现图片懒加载(lazyload) 提高用户体验(增强版)
Nov 30 #Javascript
微信小程序 欢迎界面开发的实例详解
Nov 30 #Javascript
微信小程序 简单DEMO布局,逻辑,样式的练习
Nov 30 #Javascript
微信小程序之小豆瓣图书实例
Nov 30 #Javascript
微信小程序之ES6与事项助手的功能实现
Nov 30 #Javascript
关于Vue.js 2.0的Vuex 2.0 你需要更新的知识库
Nov 30 #Javascript
You might like
php 数组的一个悲剧?
2011/05/11 PHP
一个基于PDO的数据库操作类(新) 一个PDO事务实例
2011/07/03 PHP
优化PHP程序的方法小结
2012/02/23 PHP
浅析php插件 Simple HTML DOM 用DOM方式处理HTML
2013/07/01 PHP
基于PHP的简单采集数据入库程序【续篇】
2014/07/30 PHP
javascript判断单选框或复选框是否选中方法集锦
2007/04/04 Javascript
jQuery 无刷新分页实例代码
2013/11/12 Javascript
JS获取节点的兄弟,父级,子级元素的方法
2014/01/09 Javascript
jquery 页眉单行信息滚动显示实现思路及代码
2014/06/26 Javascript
JavaScript兼容性总结之获取非行间样式案例
2016/08/07 Javascript
关于javascript获取内联样式与嵌入式样式的实例
2017/06/01 Javascript
日期时间范围选择插件:daterangepicker使用总结(必看篇)
2017/09/14 Javascript
详解Vue路由History mode模式中页面无法渲染的原因及解决
2017/09/28 Javascript
详解Vue CLI3配置解析之css.extract
2018/09/14 Javascript
详解关于element级联选择器数据回显问题
2019/02/20 Javascript
小程序开发踩坑:页面窗口定位(相对于浏览器定位)(推荐)
2019/04/25 Javascript
Flutter 超实用简单菜单弹出框 PopupMenuButton功能
2019/08/06 Javascript
[33:09]完美世界DOTA2联赛循环赛 Forest vs DM BO2第二场 10.29
2020/10/29 DOTA
python字典序问题实例
2014/09/26 Python
python如何把嵌套列表转变成普通列表
2018/03/20 Python
浅谈Python中的全局锁(GIL)问题
2019/01/11 Python
基于python实现雪花算法过程详解
2019/11/16 Python
tensorflow 限制显存大小的实现
2020/02/03 Python
CSS3下的渐变文字效果实现示例
2018/03/02 HTML / CSS
英国领先的电子、技术和办公用品购物网站:Ebuyer
2018/04/04 全球购物
党员2014两会学习心得体会
2014/03/17 职场文书
小学生学习雷锋倡议书
2014/05/15 职场文书
四风问题查摆材料
2014/08/25 职场文书
党员评议个人总结
2014/10/20 职场文书
考试作弊检讨书
2014/10/21 职场文书
夫妻分居协议书范本
2014/11/28 职场文书
中秋节慰问信
2015/02/15 职场文书
小学体育队列队形教学反思
2016/02/16 职场文书
react中的DOM操作实现
2021/06/30 Javascript
python数据可视化JupyterLab实用扩展程序Mito
2021/11/20 Python
Go gorilla securecookie库的安装使用详解
2022/08/14 Golang