深入理解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 相关文章推荐
javascript function调用时的参数检测常用办法
Feb 26 Javascript
JS.GetAllChild(element,deep,condition)使用介绍
Sep 21 Javascript
script标签属性用type还是language
Jan 21 Javascript
Node.js的Koa框架上手及MySQL操作指南
Jun 13 Javascript
AngularJS入门教程之AngularJS 模板
Aug 18 Javascript
微信小程序 rpx 尺寸单位详细介绍
Oct 13 Javascript
jQuery zTree树插件动态加载实例代码
May 11 jQuery
Vue0.1的过滤代码如何添加到Vue2.0直接使用
Aug 23 Javascript
js 实现 list转换成tree的方法示例(数组到树)
Aug 18 Javascript
Layui数据表格跳转到指定页的实现方法
Sep 05 Javascript
JS 逻辑判断不要只知道用 if-else 和 switch条件判断(小技巧)
May 27 Javascript
利用JavaScript为句子加标题的3种方法示例
Jan 05 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
雄兵连:第三季确定会出,不过时间未定,鹤熙是第三季的主角!
2020/03/13 国漫
thinkphp5 migrate数据库迁移工具
2018/02/20 PHP
张孝祥JavaScript学习阶段性总结(2)--(X)HTML学习
2007/02/03 Javascript
javascript function、指针及内置对象
2009/02/19 Javascript
jquery之empty()与remove()区别说明
2010/09/10 Javascript
收集的10个免费的jQuery相册
2011/02/26 Javascript
js中onload与onunload的使用示例
2013/08/25 Javascript
javascript单引号和双引号的区别和处理
2014/05/14 Javascript
seajs模块之间依赖的加载以及模块的执行
2016/10/21 Javascript
ES6中参数的默认值语法介绍
2017/05/03 Javascript
浅谈JS中几种轻松处理'this'指向方式
2019/09/16 Javascript
[03:15]DOTA2-DPC中国联赛1月22日Recap集锦
2021/03/11 DOTA
恢复百度云盘本地误删的文件脚本(简单方法)
2017/10/21 Python
详解python里使用正则表达式的分组命名方式
2017/10/24 Python
python解决js文件utf-8编码乱码问题(推荐)
2018/05/02 Python
基于pandas将类别属性转化为数值属性的方法
2018/07/25 Python
win10系统下Anaconda3安装配置方法图文教程
2018/09/19 Python
python 求定积分和不定积分示例
2019/11/20 Python
使用TensorFlow对图像进行随机旋转的实现示例
2020/01/20 Python
Django 404、500页面全局配置知识点详解
2020/03/10 Python
纯HTML+CSS3制作导航菜单(附源码)
2013/04/24 HTML / CSS
CSS3媒体查询Media Queries基础学习教程
2016/02/29 HTML / CSS
Canvas在超级玛丽游戏中的应用详解
2021/02/06 HTML / CSS
德国高品质男装及配饰商城:Cultizm(Raw Denim原色牛仔裤)
2018/04/16 全球购物
关于递归的一道.NET面试题
2013/05/12 面试题
教师求职信范文分享
2013/12/27 职场文书
应届毕业生简历自我评价
2014/01/31 职场文书
模具专业毕业生自荐书范文
2014/02/19 职场文书
信访维稳工作汇报
2014/10/27 职场文书
财务统计员岗位职责
2015/04/14 职场文书
给领导敬酒词
2015/08/12 职场文书
《确定位置》教学反思
2016/02/18 职场文书
apache基于端口创建虚拟主机的示例
2021/04/24 Servers
一篇文章了解正则表达式的替换技巧
2022/02/24 Javascript
Java 使用类型为Object的变量指向任意类型的对象
2022/04/13 Java/Android
MySQL索引失效场景及解决方案
2022/07/23 MySQL