深入理解jQuery

深入理解jQuery。前言

金沙8331网址,jQuery.data() 的作用是为普通对象或 DOM Element 附加数据。

在开发中经常会在DOM上存储一些自定义数据,我们可以通过setAttribute方法来实现。但是当数据为引用类型时,存储后的数据却无效。这里将用原生的JS对data方法进行实现。

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

使用setAttribute:

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

var testData = document.querySeletor;testData.setAttribute// 执行后DOM节点变化为testData.getAttribute; // => "[object Object]"

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

可以从上面的代码中看出,存进去的是个Object,取出来的是Object.toString()所产出的字符串。

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

分析

用name和value为对象附加数据

在JS经典类库-jQuery中存在data方法是通过jQuery.cache的方式进行数据存储,那么还有没有其它方法可以实现?

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

由于使用场景不同,我想实现的方式是将数据直接存储到DOM节点上,以达到使用时更方便简捷的目的。

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

那如何存储?
变量testData存储的是通过document.querySeletor获取到的Element,而Element是Object的一个实例。通过[testData instanceof Object]可以进行验证。

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

那么一切都简易了,即然是Object类型,那么就可以随意的增删自定义属性。

  obj = {}; $data; documentwrite = " + $data + '<br />'); for  { documentwrite("obj" + key + 'name = ' + obj[key]name); }  

$.data = value obj.jQuery16018518865841457738.name = value 

通过在Element的原型上增加data方法来实现DOM扩展

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

Element.prototype.data = function{ var _this = this, _dataName = 'testData', // 存储至DOM上的对象标记, 这里只是测试用名 _data = {}; // 未指定参数,返回全部 if(typeof key === 'undefined' && typeof value === 'undefined'){ return _this[_dataName]; } // setter if !== 'undefined'){ // 存储值类型为字符或数字时, 使用attr执行 var _type = typeof; if(_type === 'string' || _type === 'number'){ _this.setAttribute; } _data = _this[_dataName] || {}; _data[key] = value; _this[_dataName] = _data; return this; } // getter else{ _data = _this[_dataName] || {}; return _data[key] || _this.getAttribute; }};

var testData = document.querySelector;// 字符串类型测试testData.data;console.log); // => 'baukh'// 对象类型测试testData.data('info', {'name': 'baukh', 'age': 27});console.log); // => Object {name: "baukh", age: 27}

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

解决NodeList存储

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

现在还有一个问题,
通过Element.prototype绑定的方法只支持Element类生效,而对NodeList类并无效果.

$ = function() { var expando = "jQuery" + replace; function getData { return cache[name]; } function setData { cache[name] = value; } function getCache { obj[expando] = obj[expando] || {}; return obj[expando]; } return { data : function { var cache = getCache; if  { return getData; } else { setData; } } } }(); 

可以通过下面这些代码进行效果测试:

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

var testDataList = document.querySelectorAll; // 获取的为NodeList 而非 ElementtestDataList.data; // Uncaught TypeError: testDataList.data is not a function

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

这肯定不是想要的结果, 那么NodeList类就需要如下处理:

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

NodeList.prototype.data = function  { // setter if !== 'undefined'){ [].forEach.call(this, function  { element.data; return this; } // getter else{ return this[0].data; // getter 将返回第一个 }};

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

来测试下NodeList类的data实现:

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

var testDataList = document.querySelectorAll; // 获取的为NodeList 而非 ElementtestDataList.data; // Uncaught TypeError: testDataList.data is not a function// 字符串类型测试testDataList.data;console.log(testDataList.data; // => 'baukh'// 对象类型测试testDataList.data('info', {'name': 'baukh', 'age': 27});console.log(testDataList.data; // => Object {name: "baukh", age: 27}

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

这样就功能上就完成了.

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

当然也可以将NodeList与Element进行互换, 具体情况具体考虑.

  obj = {}; $data(obj, {name1: 'value1', name2: 'value2'}); documentwrite = " + $data + '<br />' ); documentwrite = " + $data + '<br />'); for  { documentwrite("obj" + key + 'name1 = ' + obj[key]name1 + '<br />'); documentwrite("obj" + key + 'name2 = ' + obj[key]name2); }  

$.data = value1 $.data = value2 obj.jQuery1600233050178663064.name1 = value1 obj.jQuery1600233050178663064.name2 = value2 

顺带说一下,Array类型的数据,也可以增加自定义属性。

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

var ar = [1,2,3];console.log; //true 能添加自定义属性的原因就在这里,Array也是Object的实例。ar.test1 = {a:1,b:2};console.log; //[1, 2, 3, test1: Object]console.log; //Object {a: 1, b: 2}

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

这是前端最好的时代, 这也是前端最坏的时代。 众多前端框架满天飞,随着
jQuery
在前端行业的慢慢弱化,总是会有一种斯人远去,何者慰籍的感觉。互勉吧,各位。

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

另推荐个表格组件gridManager

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

总结

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

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。

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

为 DOM Element 附加数据

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

  windowonload = function() { div = documentgetElementById; $data; documentwrite; }  

value 

测试代码中,首先通过 document.getElementById 方法获取了一个 DOM Element
,然后在这个 DOM Element 上附加了一个属性,随后就从 DOM Element
上取出了附加的属性并输出。

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

$ = function() { var expando = "jQuery" + replace; var globalCache = {}; var uuid = 0; // Other codes function getCache { if  { 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
中这刻意优化的地方非常多,在许多原本可以统一处理的对方都进行了特殊处理。但这在一定程度上,也造成了阅读源码的障碍。当然这是作者本身的编程哲学,这里就不加评论了。

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

发表评论

电子邮件地址不会被公开。 必填项已用*标注