首頁  >  文章  >  php教程  >  深入理解jQuery.data() 的實作方式

深入理解jQuery.data() 的實作方式

黄舟
黄舟原創
2016-12-12 16:27:381151瀏覽

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, &#39;name&#39;, &#39;value&#39;);  
documentwrite("$data(obj, &#39;name&#39;) = " + $data(obj, &#39;name&#39;) + &#39;<br />&#39;);     
for (var key in obj) 
{  
 documentwrite("obj" + key + &#39;name = &#39; + obj[key]name);  
 }  
 </script>

顯示結果為:

$.data(obj, &#39;name&#39;) = 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, &#39;&#39;);      
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: &#39;value1&#39;, name2: &#39;value2&#39;});     
documentwrite("$data(obj, &#39;name1&#39;) = " + $data(obj, &#39;name1&#39;) + &#39;<br />&#39; );  
documentwrite("$data(obj, &#39;name2&#39;) = " + $data(obj, &#39;name2&#39;) + &#39;<br />&#39;);     
for (var key in obj) 
{   
documentwrite("obj" + key + &#39;name1 = &#39; + obj[key]name1 + &#39;<br />&#39;);   
documentwrite("obj" + key + &#39;name2 = &#39; + obj[key]name2);  
}  
</script>

顯示結果如下:

$.data(obj, &#39;name1&#39;) = value1  
$.data(obj, &#39;name2&#39;) = 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采用了与普通对象有所不同的方式附加数据。

测试代码如下:

<div id="div_test" />     
<script type="text/javascript" src="datajs"></script>  
<script>  
windowonload = function() 
{   
div = documentgetElementById(&#39;div_test&#39;);   
$data(div, &#39;name&#39;, &#39;value&#39;);   
documentwrite($data(div, &#39;name&#39;));  
}  
</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, &#39;&#39;);   
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 中这刻意优化的地方非常多,在许多原本可以统一处理的对方都进行了特殊处理。但这在一定程度上,也造成了阅读源码的障碍。当然这是作者(及其他代码贡献者)本身的编程哲学,这里就不加评论了。


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn