深入理解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显示当前时间示例
Mar 02 Javascript
JavaScript 学习笔记之数据类型
Jan 14 Javascript
javascript中callee与caller的区别分析
Apr 20 Javascript
js基于面向对象实现网页TAB选项卡菜单效果代码
Sep 09 Javascript
jQuery获得字体颜色16位码的方法
Feb 20 Javascript
实例详解ECMAScript5中新增的Array方法
Apr 05 Javascript
自定义类似于jQuery UI Selectable 的Vue指令v-selectable
Aug 23 jQuery
vue 移动端适配方案详解
Nov 15 Javascript
浅谈目前可以使用ES10的5个新特性
Jun 25 Javascript
JavaScript实现答题评分功能页面
Jun 24 Javascript
vue+animation实现翻页动画
Jun 29 Javascript
微信小程序实现滚动Tab选项卡
Nov 16 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高级OOP技术演示
2009/08/27 PHP
整理的一些实用WordPress后台MySQL操作命令
2013/01/07 PHP
浅析Yii中使用RBAC的完全指南(用户角色权限控制)
2013/06/20 PHP
php通过ajax实现双击table修改内容
2014/04/28 PHP
php结合mysql与mysqli扩展处理事务的方法
2016/06/29 PHP
详细解读php的命名空间(二)
2018/02/21 PHP
Yii2框架redis基本应用示例
2018/07/13 PHP
20款超赞的jQuery插件 Web开发人员必备
2011/02/26 Javascript
分享一道笔试题[有n个直线最多可以把一个平面分成多少个部分]
2012/10/12 Javascript
使用jquery修改表单的提交地址基本思路
2014/06/04 Javascript
jQuery设置指定网页元素宽度和高度的方法
2015/03/25 Javascript
JavaScript正则表达式中的ignoreCase属性使用详解
2015/06/16 Javascript
Nodejs的express使用教程
2015/11/23 NodeJs
jQuery Ajax实现跨域请求
2017/01/21 Javascript
浅析Angular2子模块以及异步加载
2017/04/24 Javascript
jQuery中hover方法搭配css的hover选择器,实现选中元素突出显示方法
2017/05/08 jQuery
JavaScript限定范围拖拽及自定义滚动条应用(3)
2017/05/17 Javascript
原生JS获取元素的位置与尺寸实现方法
2017/10/18 Javascript
webpack4简单入门实例
2018/09/06 Javascript
学习jQuery中的noConflict()用法
2018/09/28 jQuery
ES6的异步终极解决方案分享
2019/07/11 Javascript
vue实现下拉加载其实没那么复杂
2019/08/13 Javascript
微信小程序之下拉列表实现方法解析(附完整源码)
2019/08/23 Javascript
Python 实现try重新执行
2019/12/21 Python
python发qq消息轰炸虐狗好友思路详解(完整代码)
2020/02/15 Python
总结python 三种常见的内存泄漏场景
2020/11/20 Python
用python对excel进行操作(读,写,修改)
2020/12/25 Python
Pycharm 如何一键加引号的方法步骤
2021/02/05 Python
Nike香港官网:Nike HK
2019/03/23 全球购物
给水排水工程专业毕业生推荐信
2013/10/28 职场文书
《小蝌蚪找妈妈》教学反思
2014/02/21 职场文书
《春晓》教学反思
2014/04/20 职场文书
幼儿园优秀班主任事迹材料
2014/05/14 职场文书
毕业设计工作总结
2015/08/14 职场文书
如何使用分区处理MySQL的亿级数据优化
2021/06/18 MySQL
css弧边选项卡的项目实践
2023/05/07 HTML / CSS