jQuery.data() は、通常のオブジェクトまたは DOM 要素にデータを添付 (および取得) するために使用されます。
以下はその実装を 3 つの部分に分けて分析します:
1. 名前と値を使用してオブジェクトにデータを追加します。つまり、最初のパラメータはデータを追加する必要があるオブジェクトです。 、2 番目のパラメータはデータの名前、3 番目のパラメータはデータの値です。もちろん、値を取得するだけの場合は、3 番目のパラメーターを渡す必要はありません。
2. 別のオブジェクトを使用してオブジェクトにデータを追加します。つまり、2 つのパラメーターを渡します。最初のパラメーターは追加する必要があるデータ オブジェクト (「obj」と呼びます)、2 番目のパラメーターも同様です。オブジェクト (「another」と呼びます); 「another」に含まれるキーと値のペアは、「obj」のデータ キャッシュ (「キャッシュ」と呼びます) にコピーされます。
3. DOM 要素にデータをアタッチします。DOM 要素もオブジェクトの一種ですが、IE6 と IE7 では、DOM 要素に直接アタッチされたオブジェクトのガベージ コレクションに問題があるため、これらのデータをグローバル キャッシュ (これをグローバル キャッシュと呼びます) に保存します。 ("globalCache")、つまり、"globalCache" には複数の DOM 要素の "キャッシュ" が含まれており、その "キャッシュ" に対応する uid を格納する属性を DOM 要素に追加します。
名前と値を使用してオブジェクトにデータを追加する
jQuery.data() を使用して通常のオブジェクトにデータを追加する場合、本質はオブジェクトに「キャッシュ」を追加し、特別な属性名を使用することです。
データを格納する「キャッシュ」もオブジェクトです。「obj」に付けたデータは実際には「キャッシュ」の属性になります。また、「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」に An を追加します。属性 (名前は "name"、値は "value") を指定し、$.data(obj, 'name') を通じて添付データを取得します。実装メカニズムをより深く理解するために、ループを使用して「obj」のプロパティを取得しました。これにより、「obj」にアタッチされている「キャッシュ」オブジェクトが実際に削除されます。
jQuery.data() が実際に「jQuery16018518865841457738」という名前のオブジェクト (名前はランダムです)、つまり「cache」に「obj」をアタッチしていることがわかります。 jquery.data() を使用してオブジェクトに付加された属性は、実際にはこの「キャッシュ」の属性になります。
次のコードを使用して同様の関数を実現できます:
$ = 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 のコードの最初の行は、「jQuery1.6」に乱数 (0.xxxx) を加えた「expando」を定義します。そして、数値以外の部分を削除します。この形式は jQuery の他の場所で使用されるため、ここでは説明しません。これは特別な名前であり、異なるページ (異なる iframe など) を識別するために使用できることだけを理解する必要があります。 ) 「expando」が違いを生みます)。
次に、データを取得するための関数 getData() が定義されています。これは、実際には「cache」から属性を取得するもので、cache[name] を返します。
次に、「cache」のプロパティを設定するために使用される setData() 関数があります。実際には、cache[name] の値を設定します。
の後に getCache() が続き、「obj」の「キャッシュ」を取得します。つまり、obj[expando] が空の場合は、それを初期化します。
最後に、data メソッドが公開されます。まず、受信した「obj」に従って、2 つのパラメーターが渡されたときに getData() メソッドが呼び出されます。に渡されると、setData() メソッドが呼び出されます。
別のオブジェクトを使用してオブジェクトにデータを追加します
名前と値を指定して値を割り当てることに加えて、別のオブジェクト (「別の」) をパラメータとして直接渡すこともできます。この場合、「another」の属性名と属性値は複数のキーと値のペアとして扱われ、そこから抽出された「名前」と「値」が対象オブジェクトのキャッシュにコピーされます。
機能テスト コードは次のとおりです:
<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
上記のテスト コードでは、最初に 2 つのキーを持つ「別の」オブジェクトを渡します。次に、$.data(obj, 'name1') と $.data(obj, 'name2') を使用して追加のデータを取得します。メカニズムを深く理解するために、「」をトラバースして隠しデータを取り出しました。 obj」「cache」オブジェクトを取得し、「cache」オブジェクトの「name1」属性と「name2」属性の値を取得しました。
jQuery.data() が実際に「obj.jQuery1600233050178663064」という名前のオブジェクトを「obj」、つまり「キャッシュ」にアタッチしていることがわかります。 jquery.data() を使用して渡されたキーと値のペアは、「キャッシュ」にコピーされます。
次のコードを使用して同様の機能を実現できます:
$ = 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采用了与普通对象有所不同的方式附加数据。
测试代码如下:
<div id="div_test" /> <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 中这刻意优化的地方非常多,在许多原本可以统一处理的对方都进行了特殊处理。但这在一定程度上,也造成了阅读源码的障碍。当然这是作者(及其他代码贡献者)本身的编程哲学,这里就不加评论了。