網上有很多教你怎么使用jQuery.data(..)來實現數據緩存,但有兩個用戶經常使用的data([key],[value])和jQuery.data(element,[key],[value])幾乎沒有什么文章說清楚它們兩的區別,所以我用到了,研究下分享給大家。
$("").data([key],[value])與jQuery.data(element,[key],[value])的區別
這兩個函數都是用來在元素上存放數據也就平時所說的數據緩存,都返回jQuery對象,當時我分別在使用它倆的時候真的嚇我一跳,區別可大了,真是不用不知道,一用嚇一跳。看例子先吧,后再根據源代碼分析。
- <div id="test2" onclick="test()">test2</div>
- <div id="abc3" onclick="test()">test3</div>
- <div id="test" onclick="test()">test</div>
- <p id="ttt">aaaa</p>
- <script>
- $(document).ready(function(){
- $("#test").click(function(){
- alert("JQUERY");
- var e=$("div");//定義了兩jquery對象
- var w=$("div");//e是不等於w的。
- //首先使用data([key],[value])用法。
- $(e).data("a","aaaa");//分別在e和w上保存Key一樣的數據,
- $(w).data("a","wwww");// 看它是否會覆蓋前面的,雖然是保存在不同對象上。
- alert($(e).data("a"));//你猜到答案了嗎,里輸出是wwww;是不是有點意外?
- alert(e===w)//false
- alert($(w).data("a"));//這里也是wwww;
- //使用jQuery.data(element,[key],[value])來存放數據。
- $.data(e,"b","cccc");//分別在e和w上保存Key一樣的數據,
- $.data(w,"b","dddd");// 看它是否會覆蓋前面的,雖然是保存在不同對象上。
- alert($.data(e,"b"));//應該你能猜答案吧,輸出cccc
- alert($.data(w,"b"));//這輸出dddd
- });
- });
- </script>
看了上面的例子是不是發現data([key],[value])與jQuery.data(element,[key],[value])兩個根本就不一樣了對吧?它們之間到底有沒有關系呢。怎么data([key],[value])會覆蓋前面key相同的值呢?
而jQuery.data(element,[key],[value])只要是綁定到不同的對象上都不會造成覆蓋。是這樣嗎?那來研究下它們的源代碼吧。
先看jQuery.data(element,[key],[value])源代碼。
- jQuery.extend({
- cache: {},
- // Please use with caution
- uuid: 0,
- // Unique for each copy of jQuery on the page
- // Non-digits removed to match rinlinejQuery
- expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
- ....
- data: function( elem, name, data, pvt /* Internal Use Only */ ) {
- // 是否可以附加數據,不可以則直接返回
- if ( !jQuery.acceptData( elem ) ) {
- return;
- }
- var privateCache, thisCache, ret,
- //jQuery.expando這是一個唯一的字符串,是這介jquery對象產生的時候就生成了。
- internalKey = jQuery.expando,
- getByName = typeof name === "string",
- // 必須區分處理DOM元素和JS對象,因為IE6-7不能垃圾回收對象跨DOM對象和JS對象進行的引用屬性
- isNode = elem.nodeType,
- // 如果是DOM元素,則使用全局的jQuery.cache
- // 如果是JS對象,則直接附加到對象上
- cache = isNode ? jQuery.cache : elem,
- // Only defining an ID for JS objects if its cache already exists allows
- // the code to shortcut on the same path as a DOM node with no cache
- id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,
- isEvents = name === "events";
- // 避免做更多的不必要工作,當嘗試在一個沒有任何數據的對象上獲取數據時
- // 對象沒有任何數據,直接返回
- if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
- return;
- }
- // id不存在的話就生成一個
- if ( !id ) {
- // Only DOM nodes need a new unique ID for each element since their data
- // ends up in the global cache
- if ( isNode ) {
- // 如果是DOM元素則在元素上產生唯一的ID 並且以jQuery.expando
- //為屬性值為id保存在elem元素上,以便以后再根據jQuery.expando來查找ID。
- elem[ internalKey ] = id = ++jQuery.uuid;
- } else {
- // JS對象則直接使用jQuery.expando,既然是直接附加到對象上,又何必要id呢?
- // 避免與其他屬性沖突!
- id = internalKey;
- }
- }
- //// 當我們試着訪問一個鍵是否含有值的時候,如果不存在jQuery.cache[id]值,
- // 初始化jQuery.cache[id]值 為一個空對象{}
- if ( !cache[ id ] ) {
- cache[ id ] = {};
- if ( !isNode ) {
- cache[ id ].toJSON = jQuery.noop;
- }
- }
- // An object can be passed to jQuery.data instead of a key/value pair; this gets
- // shallow copied over onto the existing cache
- // data是接收對象和函數,淺拷貝
- if ( typeof name === "object" || typeof name === "function" ) {
- if ( pvt ) {
- cache[ id ] = jQuery.extend( cache[ id ], name );
- } else {
- cache[ id ].data = jQuery.extend( cache[ id ].data, name );
- }
- }
- / 存儲對象,存放了所有數據的映射對象
- privateCache = thisCache = cache[ id ];
- // jQuery data() is stored in a separate object inside the object's internal data
- // cache in order to avoid key collisions between internal data and user-defined
- // data.
- // jQuery內部數據存在一個獨立的對象(thisCache.data==thisCache[ internalKey ])
- //上,為了避免內部數據和用戶定義數據沖突
- if ( !pvt ) {
- // 存放私有數據的對象不存在,則創建一個{}
- if ( !thisCache.data ) {
- thisCache.data = {};
- }
- // 使用私有數據對象替換thisCache
- thisCache = thisCache.data;
- }
- // 如果data不是undefined,表示傳入了data參數,則存儲data到name屬性上
- if ( data !== undefined ) {
- // jQuery.camelCase( name )作用是如果傳入的是object/function,不做轉換,
- //只有傳入的name是字符串才會轉換。所以最終保存下來的是key/value對;
- thisCache[ jQuery.camelCase( name ) ] = data;
- }
- //從這以后下面的代碼都是處理data: function( elem, name)data為空,求返回值data的情況了。
- if ( isEvents && !thisCache[ name ] ) {
- return privateCache.events;
- }
- // 如果name是字符串,則返回data
- // 如果不是,則返回整個存儲對象
- if ( getByName ) {
- // First Try to find as-is property data
- ret = thisCache[ name ];
- // Test for null|undefined property data
- if ( ret == null ) {
- // Try to find the camelCased property
- ret = thisCache[ jQuery.camelCase( name ) ];
- }
- } else {
- ret = thisCache;
- }
- return ret;
- },
- ............
- });
請看圖。
看jQuery.data(element,[key],[value])源代碼后可以知道,每一個element都會有自己的一個{key:value}對象保存着數據,所以新建的對象就算有key相同它也不會覆蓋原來存在的對象key所對應的value,因為新對象保存是是在另一個{key:value}對象中。
接下來要分析data([key],[value])源代碼使用到了each(callback),在分析它之前先看下each(callback)用法和源代碼。
- <div id="test2" onclick="test()">test2</div>
- <div id="abc3" onclick="test()">test3</div>
- <div id="test" onclick="test()">test</div>
- <p id="ttt">aaaa</p>
- <script>
- $(document).ready(function(){
- $("#test").click(function(){
- alert("JQUERY");
- var i=0;
- $("#abc3").each(function() {
- alert(++i);//只輸出1;因為只有一個<div id="abc3">
- });
- alert("----");
- var j=0;
- $("div").each(function() {
- alert(++j);//分別輸出1,2,3;因為有三個<div>所以循環三遍
- });
- });
- });
- </script>
- 現在來看each方法的具體實現如下:
- jQuery.fn = jQuery.prototype = {
- each: function( callback, args ) {
- return jQuery.each( this, callback, args );
- }
- }
- 可以看到它返回的是全局的each方法,並且將自身jQuery對象做為參數給它,全局的each方法的具體實現如下:
- // args 作為內部成員的調用來使用
- each: function( object, callback, args ) {
- var name, i = 0, length = object.length; // 當object為jQuery對象時,length非空
- if ( args ) {
- if ( length === undefined ) {
- for ( name in object )
- if ( callback.apply( object[ name ], args ) === false )
- break;
- } else
- for ( ; i < length; )
- if ( callback.apply( object[ i++ ], args ) === false )
- break;
- // 以下是客戶端程序進行調用
- } else {
- if ( length === undefined ) {
- for ( name in object )
- if ( callback.call( object[ name ], name, object[ name ] ) === false )
- break;
- } else
- // i表示索引值,value表示DOM元素
- for ( var value = object[0];
- i < length && callback.call( value, i, value ) !== false;
- value = object[++i] ){}
- }
- return object;
- }
現在我們關注下 for ( var value = object[0]; i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} 這句代碼;其中object[0]取得jQuery對象中的第一個DOM元素,通過for循環,
得到遍歷整個jQuery對象中對應的每個DOM元素,通過callback.call( value,i,value); 將callback的this對象指向value對象,並且傳遞兩個參數,i表示索引值,value表示DOM元素;其中callback是類似於 function(index, elem) { } 的方法。所以就得到 $("").each(function(index, elem){ });
再來看看data([key],[value])的源代碼。
- jQuery.fn.extend({
- data: function( key, value ) {
- var parts, part, attr, name, l,
- elem = this[0],
- i = 0,
- data = null;
- // Gets all values
- if ( key === undefined ) {
- .....//處理沒有Key的情況,這里不是我們要討論的
- return data;
- }
- // Sets multiple values
- if ( typeof key === "object" ) {
- return this.each(function() {
- jQuery.data( this, key );
- });
- }
- parts = key.split( ".", 2 );
- parts[1] = parts[1] ? "." + parts[1] : "";
- part = parts[1] + "!";
- return jQuery.access( this, function( value ) {
- if ( value === undefined ) {
- 。。。//這里是沒有value時,是索取返回值的情況,這不是我們討論
- }
- parts[1] = value;
- //如果我使用用$("div").data("a","aaa")),下面調用each前的this指的是$("div")這返回的對象,
- this.each(function() {//注意了,這里是以每一個匹配的元素作為上下文來執行一個函數
- var self = jQuery( this );
- self.triggerHandler( "setData" + part, parts );
- //這里在元素上存放數據,本質還是委托data(element,[key],[value])來做的。
- //看前面有分析過了。
- //下面data( this, key, value )里的this指的是遍歷整個jQuery對象中對應的每個DOM元素
- //$("div")它對應頁面中一個<div>數組。
- jQuery.data( this, key, value )<span style="#ffcc00;">;//這名句會被循環多次執行,也就是保存數據</span>。
- //這里就是核心一句話。但要清楚看上面了它是在each(functipn(){})中的。
- self.triggerHandler( "changeData" + part, parts );
- });
- }, null, value, arguments.length > 1, null, false );
- },
- //在元素上移除存放的數據。具體實現如下:
- removeData: function( key ) {
- return this.each(function() {
- jQuery.removeData( this, key );
- });
- }
- });
如果對於data([key],[value])的源代碼不是很了解,好吧,我就用一個例子來模仿實現它吧。
- <div id="test2" onclick="test()">test2</div>
- <div id="abc3" onclick="test()">test3</div>
- <div id="test" onclick="test()">test</div>
- <p id="ttt">aaaa</p>
- <script>
- $(document).ready(function(){
- $("#test").click(function(){
- alert("JQUERY");
- var i=0;
- $("#abc3").each(function() {
- alert(++i);//只輸出1;因為只有一個<div id="abc3">
- });
- alert("----");
- var j=1;
- $("div").each(function() {//以每一個匹配的元素作為上下文來執行這個函數
- $.data(this,"a","wwww");//這里的this就是指$("div"),
- //分別遍歷每一個匹配的元素給它們每一個對象{}都保存一個key/value
- alert(j++);//分別輸出1 ,2 ,3 因為有三個<div>元素
- });
- alert($("#test").data("a"));//返回wwww,
- //是不是很驚呀,我沒有保存在它身上啊,怎么也有值,很明顯是它是查這個div節點上有沒有,
- //肯定是有值了,因為上面給循環保存在div這Dom結點上了。
- alert($("#test")===$("div"));//false證明兩新建的對象不是同一個。
- alert($("div").data("a"));//返回wwww,
- //這里也是一樣因為是div節點上都保存了"a"="wwww"這樣一個鍵值對了。
- });
- });
- </script>
現在對data([key],[value])與jQuery.data(element,[key],[value])都有了解了吧,如果還是半懂,再回頭多看一遍,耐心地理解一下。其實表面上很不一樣。但本質上還是有聯系的,現在明白原理后就可以請放心地使用了。jQuery.data(element,[key],[value])只把數據綁定到參數element節點上。data([key],[value])
如$("div").data("a","aaaa")它是把數據綁定每一個匹配div節點的元素上。
附加說明下,文中所分析用到的是jquery-1.7.2.js的源代碼。如果你想了解更多請自行去下載