深入理解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 相关文章推荐
求解开jscript.encode代码的asp函数
Feb 28 Javascript
jQuery插件开发全解析
Oct 10 Javascript
用javascript添加控件自定义属性解析
Nov 25 Javascript
js使用onmousemove和onmouseout获取鼠标坐标的方法
Mar 31 Javascript
浅谈Sizzle的“编译原理”
Apr 14 Javascript
JQuery中Bind()事件用法分析
May 05 Javascript
浅谈JavaScript 中有关时间对象的方法
Aug 15 Javascript
jQuery+json实现动态创建复杂表格table的方法
Oct 25 Javascript
JS正则表达式验证密码格式的集中情况总结
Feb 23 Javascript
ES6 let和const定义变量与常量的应用实例分析
Jun 27 Javascript
JS动态图片的实现方法完整示例
Jan 13 Javascript
Vue单文件组件开发实现过程详解
Jul 30 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/04 日漫
PHP之数组学习
2011/05/29 PHP
php设计模式 Composite (组合模式)
2011/06/26 PHP
Php中用PDO查询Mysql来避免SQL注入风险的方法
2013/04/25 PHP
php操作MongoDB基础教程(连接、新增、修改、删除、查询)
2014/03/25 PHP
symfony表单与页面实现技巧
2015/01/26 PHP
PHP 微信支付类 demo
2015/11/30 PHP
jQuery选择没有colspan属性的td的代码
2010/07/06 Javascript
jQuery基于当前元素进行下一步的遍历
2014/05/20 Javascript
nodejs npm包管理的配置方法及常用命令介绍
2014/06/05 NodeJs
微信JS接口汇总及使用详解
2015/01/09 Javascript
关于JS中prototype的理解
2015/09/07 Javascript
Extjs 点击复选框在表格中增加相关信息行
2016/07/12 Javascript
详解照片瀑布流效果(js,jquery分别实现与知识点总结)
2017/01/01 Javascript
在javaScript中检测数据类型的几种方式小结
2017/03/04 Javascript
javascript过滤数组重复元素的实现方法
2017/05/03 Javascript
Angular2使用vscode断点调试ts文件的方法
2017/12/13 Javascript
使用Vue写一个datepicker的示例
2018/01/27 Javascript
JavaScript常用截取字符串的三种方式用法区别实例解析
2018/05/15 Javascript
解决angularjs中同步执行http请求的方法
2018/08/13 Javascript
在vue中解决提示警告 for循环报错的方法
2018/09/28 Javascript
利用React Router4实现的服务端直出渲染(SSR)
2019/01/07 Javascript
vue实现一拉到底的滑动验证
2019/07/25 Javascript
Jquery 动态添加元素并添加点击事件实现过程解析
2019/10/12 jQuery
three.js显示中文字体与tween应用详析
2021/01/04 Javascript
python中ConfigParse模块的用法
2014/09/29 Python
Python中多个数组行合并及列合并的方法总结
2018/04/12 Python
Python 读取xml数据,cv2裁剪图片实例
2020/03/10 Python
使用HTML5 Canvas绘制圆角矩形及相关的一些应用举例
2016/03/22 HTML / CSS
HTML5单选框、复选框、下拉菜单、文本域的实现代码
2020/12/01 HTML / CSS
加拿大当代时尚服饰、配饰和鞋类专业零售商和制造商:LE CHÂTEAU
2017/10/06 全球购物
英国领先的露营和露营车品牌之一:OLPRO
2019/08/06 全球购物
西安交大自主招生自荐信
2014/01/27 职场文书
安全负责人任命书
2014/06/06 职场文书
python实现过滤敏感词
2021/05/08 Python
javascript进阶篇深拷贝实现的四种方式
2022/07/07 Javascript