jQuery1.9.1源码分析--数据缓存Data模块

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jQuery1.9.1源码分析--数据缓存Data模块相关的知识,希望对你有一定的参考价值。

jQuery1.9.1源码分析--数据缓存Data模块

阅读目录

jQuery API中Data的基本使用方法介绍

     jQuery的数据缓存模块是以一种安全的方式为DOM元素附加任意类型的数据,避免了javascript和DOM之间相互引用而导致的内存泄露问题;
当然我们在日常使用中可以给元素设置属性,比如使用attr等方法,效果是一致的,但是使用该方法会直接暴露数据到源码html元素上,但是同时也是该缺点
获取对于数据安全性不是很高的话,也是一个优点,因为对于开发来讲很直观,便捷,直接可以看到数据的增加或者删除.
但是使用attr也可以列出如下坏处:

1. 循环引用;
2. 直接暴露数据,数据的安全性需要考虑;
3. 增加一堆的自定义标签属性,对浏览器渲染没有很大的意义;
4. 设置值或者获取值的时候,需要对html元素dom节点操作;

基于上面四点的缺点,我们或许可以考虑使用数据缓存Data属性来操作;下面来介绍下该数据缓存Data;
jquery整体结构源码如下:
jQuery.extend({
    // 全局缓存对象
    cache: {},

    // 页面中每个jquery副本的唯一标识
    expando:"..",

    noData: {},

    // 是否有关联的数据
    hasData: function( elem ) {},

    // 设置,读取自定义数据
    data: function( elem, name, data ) {},

    // 移除自定义数据
    removeData: function( elem, name ) {},

    // 设置或读取内部数据
    _data: function( elem, name, data ) {},

    // 是否可以设置数据
    acceptData: function( elem ) {}
});
jQuery.fn.extend({
    // 设置,读取自定义数据,解析html5属性 data-
    data: function( key, value ){},

    // 移除自定义数据
    removeData: function( key ){}
});
// 解析html5属性 data-
function dataAttr( elem, key, data ){}

// 检查数据缓存对象是否为空
function isEmptyDataObject( obj ) {}

jQuery.extend({
    // 清空数据缓存对象
    cleanData: function(elems, acceptData){}
})
我们先来看看jquery API有关Data的相应的方法介绍;
1. jQuery.data()
存储任意数据到指定的元素或者返回设置的值;
存储任意数据到指定的元素上如下1
1. jQuery.data( element, key, value )
该方法是:存储任意数据到指定的元素,返回设置的值。
@param element {Element} 要存储数据的DOM对象
@param key {string} 存储的数据名
@param value {object} 新数据值
jQuery.data() 方法允许我们在DOM元素上附加任意类型的数据,避免了循环引用的内存泄漏风险。如果 DOM 元素是通过
jQuery 方法删除的或者当用户离开页面时,jQuery 同时也会移除添加在上面的数据。
如下测试代码:
<div></div>
<script>
     var div = $("div")[0];
     jQuery.data(div, "test", { first: 16, last: "pizza!" });
     console.log(jQuery.data(div, "test").first); // 16
     console.log(jQuery.data(div, "test").last);  // pizza
</script>
     返回指定元素上响应名字的数据.如下2
2. jQuery.data( element, key )
作用: 返回用jQuery.data(element, key, value)储存在元素上的相应名字的数据,或者元素上完整的数据存储.
它有2种形式 如下:
1. jQuery.data( element, key )
@param element 要关联数据的DOM对象
@param key {string} 存储的数据名
这是一个底层的方法,你也可用更方便的 .data()方法, 和.data()方法一样.
2. jQuery.data( element )
@param element 要关联数据的DOM对象
jQuery.data(element)时将获取一个JavaScript对象,它包含了元素上所有存储的数据。

2. .data()---(JQuery实例上实现的方法);
在匹配元素上存储任意相关数据 或 返回匹配的元素集合中的第一个元素的给定名称的数据存储的值。
有2种设置值的方式;如下:
1. .data( key, value )
@param {string} key 一个字符串,用户存储数据的名称。
@param value 新的数据值;它可以是除了undefined任意的Javascript数据类型。
2. .data( obj )
@param obj {object} 一个用于更新数据的 键/值对

实例演示demo如下:
我们可以在一个元素上设置不同的值,之后获取这些值:
$("body").data("foo", 52);
$("body").data("bar", { myType: "test", count: 40 });
$("body").data({ baz: [ 1, 2, 3 ] });

console.log($("body").data("foo")); // 52
console.log($("body").data()); // { foo: 52, bar: { myType: "test", count: 40 }, baz: [ 1, 2, 3 ] }

// 获取data中的某一个对象的值
console.log($("body").data("bar").myType); // test
  3. .data(key);
返回匹配的元素集合中的第一个元素的给定名称的数据存储的值。 通过.data(name, value)或HTML5 data-* 属性设置;
比如如下代码:
console.log( $("body").data("foo")); //undefined
$("body").data("bar", "foobar");
console.log( $("body").data("bar")); //foobar
如果那个元素上没有设置任何值,那么将返回undefined。比如上面的foo;

HTML5 data-* Attributes(HTML5 data-* 属性)
测试代码如下:
<div data-role="page" data-last-value="43" data-hidden="true" data-options=‘{"name":"John"}‘></div>
      var role = $("div").data("role");
      var lastValue = $("div").data("lastValue");
      var hidden = $("div").data("hidden");
      var options = $("div").data("options").name;
      console.log(role === "page");  // true
      console.log(lastValue === 43); // true
      console.log(hidden === true);  // true
      console.log(options === "John");  // true
      该元素的data-last-value属性。 如果没有传递key参数的数据存储, jQuery将在元素的属性中搜索, 将驼峰式字符串转化为中横线字符串,
然后在结果前面加上data-。 所以,该字符串lastValue将被转换为data-last-value。

4. jQuery.hasData( element )
一个用于进行检查数据的DOM元素。返回值是布尔值true或者false
jQuery.hasData()方法提供了一种方法来确定一个元素是否有任何数据,这些数据是使用jQuery.data()设置的。如果一个元素没有关联
的data对象,该方法返回false ;否则返回true 。

请注意,jQuery的事件系统是使用jQuery数据 存储事件处理程序的。 因此,使用.on(), .bind(), .live(), .delegate(),
或一个速记事件方法 绑定事件到一个元素上的时候,也会在那个元素上关联一个 data 对象。
如下测试代码:
 <p>Results: </p>
      <script>
          var $p = jQuery("p"), p = $p[0];

          console.log(jQuery.hasData(p)+" "); // false

          $.data(p, "testing", 123);
          console.log(jQuery.hasData(p)+" "); // true

          $.removeData(p, "testing");
          console.log(jQuery.hasData(p)+" "); // false

          // 使用jQuery数据 存储事件处理程序 绑定事件到一个元素上的时候,也会在那个元素上关联一个 data 对象
          $p.on(‘click‘, function() {});
          console.log(jQuery.hasData(p)+" "); // true

          $p.off(‘click‘);
          console.log(jQuery.hasData(p)+" "); // false
      </script>
  5. jQuery.removeData( element [, name ] )
删除一个先前存储的数据片段。
@param element 要移除数据的DOM对象
@param name 要移除的存储数据名.
注意这是一个底层的方法,你应该用.removeData()代替,.removeData()是原型实例上的方法.
jQuery.removeData()方法允许我们移除用jQuery.data()绑定的值。当带name参数调用的时候,jQuery.removeData()将删除那个特有的值,
当不带任何参数的时候,所有的值将被移除。
测试代码如下:
     <div>value1 before creation: <span></span></div>
     <div>value1 after creation: <span></span></div>
     <div>value1 after removal: <span></span></div>
     <div>value2 after removal: <span></span></div>
     <script>
         var div = $("div")[0];
         $("span:eq(0)").text("" + $("div").data("test1")); // value1 before creation: undefined
         jQuery.data(div, "test1", "VALUE-1");
         jQuery.data(div, "test2", "VALUE-2");
         $("span:eq(1)").text("" + jQuery.data(div, "test1")); // value1 after creation: VALUE-1
         jQuery.removeData(div, "test1");
         $("span:eq(2)").text("" + jQuery.data(div, "test1")); // value1 after removal: undefined
         $("span:eq(3)").text("" + jQuery.data(div, "test2")); // value2 after removal: VALUE-2
     </script>
  6. .removeData( [name ] )
在元素上移除绑定的数据.
@param {name} 要移除的存储数据名.
.removeData()方法允许我们移除用.data()绑定的值。当带name参数调用的时候,.removeData()将删除那个特有的值,当不带任何参数的时候,
.removeData()将移除所有的值。
需要注意的是.removeData()仅会删除来自jQuery内部.data()缓存中的数据, 并且元素上任何相应的data-属性不会被删除。
但可以使用.removeAttr()来移除data-属性。
测试代码如下:
<div>value1 before creation: <span></span></div>
<div>value1 after creation: <span></span></div>
<div>value1 after removal: <span></span></div>
<div>value2 after removal: <span></span></div>
<script>
         var div = $("div")[0];
         $("span:eq(0)").text("" + $("div").data("test1")); // value1 before creation: undefined
         jQuery.data(div, "test1", "VALUE-1");
         jQuery.data(div, "test2", "VALUE-2");
         $("span:eq(1)").text("" + $("div").data("test1")); // value1 after creation: VALUE-1
         $("div").removeData("test1");
         $("span:eq(2)").text("" + $("div").data("test1")); // value1 after removal: undefined
         $("span:eq(3)").text("" + $("div").data("test2")); // value2 after removal: VALUE-2
 </script>

jQuery.acceptData(elem)源码分析

数据缓存对象源码分析如下:
1. jQuery.acceptData(elem)
该方法用于判断DOM元素是否可以设置数据;相关源代码如下:
jQuery.extend({

         noData: {
             "embed": true,
             // Ban all objects except for Flash (which handle expandos)
             "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
             "applet": true
         },

         // 是否可以设置数据
         acceptData: function( elem ) {
             // Do not set data on non-element because it will not be cleared (#8335).
             if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) {
                 return false;
             }

             var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];

             // nodes accept data unless otherwise specified; rejection can be conditional
             return !noData || noData !== true && elem.getAttribute("classid") === noData;
         }
     });
1. 首先判断该元素是否是节点,且判断是不是元素节点,和文档(根节点)节点,如果都不是,则直接返回false;
2. jQuery.noData中存放了不支持扩展属性的embed,object和applet, 该三个元素是不支持设置数据的;但是object元素,还需要检查其属性
classid值来判断是不是Flash, 如果是Flash的话, 就可以支持设置数据的.

jQuery.data(elem, name, data)源码分析

源码如下:

jQuery.extend({
        data: function( elem, name, data ) {
            return internalData( elem, name, data );
        }
})

具体可以看 internalData 方法的源码分析

internalRemoveData方法源码分析

jQuery.extend({
removeData: function( elem, name ) {
return internalRemoveData( elem, name );
}
});
该方法通过移除使用jQuery.data()设置的数据,该方法的功能也是取决于参数的个数和类型;目前共有3种用法:
1. jQuery.removeData(elem)
如果没有传入参数name的话,则移除DOM关联的所有数据;
2. jQuery.removeData(elem,name)
如果传入了参数name的话,则移除DOM元素关联的指定name属性的数据;
3. jQuery.removeData(elem,list)
第二个参数还可以是数据名数组或者使用空格分割的多个数据名,用于一次性移除掉;
该方法执行三个步骤如下:
a. 通过关联id找到对应的数据缓存对象;
b. 如果传入参数name,则从数据缓存对象中移除一个或者多个数据.
c. 如果数据缓存中没有数据,则销毁这个对象.
1. 删除自定义数据对象的cache[id].data;
2. 删除数据缓存对象cache[id];
3. 删除dom元素扩展的jQuery.expando属性.

源码如下:

function internalRemoveData( elem, name, pvt ) {
        if ( !jQuery.acceptData( elem ) ) {
            return;
        }

        var i, l, thisCache,
            isNode = elem.nodeType,

            // See jQuery.data for more information
            cache = isNode ? jQuery.cache : elem,
            id = isNode ? elem[ jQuery.expando ] : jQuery.expando;

        // If there is already no cache entry for this object, there is no
        // purpose in continuing
        if ( !cache[ id ] ) {
            return;
        }

        if ( name ) {

            thisCache = pvt ? cache[ id ] : cache[ id ].data;

            if ( thisCache ) {

                // Support array or space separated string names for data keys
                if ( !jQuery.isArray( name ) ) {

                    // try the string as a key before any manipulation
                    if ( name in thisCache ) {
                        name = [ name ];
                    } else {

                        // split the camel cased version by spaces unless a key with the spaces exists
                        name = jQuery.camelCase( name );
                        if ( name in thisCache ) {
                            name = [ name ];
                        } else {
                            name = name.split(" ");
                        }
                    }
                } else {
                    // If "name" is an array of keys...
                    // When data is initially created, via ("key", "val") signature,
                    // keys will be converted to camelCase.
                    // Since there is no way to tell _how_ a key was added, remove
                    // both plain key and camelCase key. #12786
                    // This will only penalize the array argument path.
                    name = name.concat( jQuery.map( name, jQuery.camelCase ) );
                }

                for ( i = 0, l = name.length; i < l; i++ ) {
                    delete thisCache[ name[i] ];
                }

                // If there is no data left in the cache, we want to continue
                // and let the cache object itself get destroyed
                if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
                    return;
                }
            }
        }

        // See jQuery.data for more information
        if ( !pvt ) {
            delete cache[ id ].data;

            // Don‘t destroy the parent cache unless the internal data object
            // had been the only thing left in it
            if ( !isEmptyDataObject( cache[ id ] ) ) {
                return;
            }
        }

        // Destroy the cache
        if ( isNode ) {
            jQuery.cleanData( [ elem ], true );

        // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
        } else if ( jQuery.support.deleteExpando || cache != cache.window ) {
            delete cache[ id ];

        // When all else fails, null
        } else {
            cache[ id ] = null;
        }
     }
该方法有三个参数
1. elem: 待移除的DOM元素或javascript对象;
2. name: 待移除的数据名,可以是单个数据名,数据名数组,也可以是使用空格分开的多个数据名;
3. pvt: 指定移除的数据是内部数据还是自定义的数据,如果为true的话,说明是内部数据,否则的话是自定义数据;
源码分析如下:
a. 如果参数不支持设置属性的话,则直接返回;如下代码:
if ( !jQuery.acceptData( elem ) ) {
return;
}
b. 定义局部变量;源码如下:
var i, l, thisCache,
isNode = elem.nodeType,
// See jQuery.data for more information
cache = isNode ? jQuery.cache : elem,
id = isNode ? elem[ jQuery.expando ] : jQuery.expando;

thisCache指向DOM元素或javascript关联的数据缓存对象,如果参数pvt为true的话,则指向了内部的缓存对象,否则的话,指向了自定义的
数据缓存对象.
cache 指向存储数据对象;
isNode 节点的类型;
取出关联id,对于DOM元素而言,关联id是elem[ jQuery.expando ],对于javascript对象的话,则是jQuery.expando
c. 如果数据缓存对象关联的id不存在的话,则直接返回;如下源码:
if ( !cache[ id ] ) {
return;
}
d. 如果传入了参数name,则移除一个或者多个数据,源码如下:
if ( name ) {

       thisCache = pvt ? cache[ id ] : cache[ id ].data;

       if ( thisCache ) {

           // Support array or space separated string names for data keys
           if ( !jQuery.isArray( name ) ) {

               // try the string as a key before any manipulation
               if ( name in thisCache ) {
                   name = [ name ];
               } else {

                   // split the camel cased version by spaces unless a key with the spaces exists
                   name = jQuery.camelCase( name );
                   if ( name in thisCache ) {
                       name = [ name ];
                   } else {
                       name = name.split(" ");
                   }
               }
           } else {
               // If "name" is an array of keys...
               // When data is initially created, via ("key", "val") signature,
               // keys will be converted to camelCase.
               // Since there is no way to tell _how_ a key was added, remove
               // both plain key and camelCase key. #12786
               // This will only penalize the array argument path.
               name = name.concat( jQuery.map( name, jQuery.camelCase ) );
           }

           for ( i = 0, l = name.length; i < l; i++ ) {
               delete thisCache[ name[i] ];
           }

           // If there is no data left in the cache, we want to continue
           // and let the cache object itself get destroyed
           if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
               return;
           }
       }
   }
如上代码: thisCache = pvt ? cache[ id ] : cache[ id ].data;
如果pvt为true的话,表示需要移除的是内部数据,则变量thisCache指向与内部数据缓存对象cache[id]; 如果pvt为false的话,
表示需要移除的时自定义数据,则变量thisCache指向了自定义数据 cache[id].data;
如果数据存在的话,则移除数据,如下if判断是否存在;
if ( thisCache ) {
}
如果参数name不是数组的话,源码如下:
if ( !jQuery.isArray( name ) ) {}
如果参数name在数据缓存对象thisCache对象存在的话,则封装为数组形式,如下代码:
if ( name in thisCache ) {
name = [ name ];
}
否则把参数name转换为驼峰形式,如果驼峰形式name在数据缓存对象thisCache中存在的话,则封装为[驼峰name],否则使用空格分隔
参数name,最后得到含有多个数据名的数组;源码如下:
// split the camel cased version by spaces unless a key with the spaces exists
   name = jQuery.camelCase( name );
   if ( name in thisCache ) {
       name = [ name ];
   } else {
       name = name.split(" ");
   }
如果参数name是数组的话,合并name属性成为数组形式;
name = name.concat( jQuery.map( name, jQuery.camelCase ) );

接着遍历参数中的name的数据名,使用运算符delete逐个从数据缓存对象this.cache中删除掉.源码如下:
for ( i = 0, l = name.length; i < l; i++ ) {
delete thisCache[ name[i] ];
}
如果thisCache对象中仍有数据的话,则直接返回;如果参数pvt为true的话,则需要调用isEmptyDataObject判断thisCache对象中是否还有
数据,否则调用 jQuery.isEmptyObject 检查数据缓存对象this.cache 是否为空对象;
如下代码:
// If there is no data left in the cache, we want to continue
// and let the cache object itself get destroyed
if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
return;
}
isEmptyDataObject方法的源码如下:
// checks a cache object for emptiness
   function isEmptyDataObject( obj ) {
       var name;
       for ( name in obj ) {

           // if the public data object is empty, the private is still empty
           if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
               continue;
           }
           if ( name !== "toJSON" ) {
               return false;
           }
       }

       return true;
   }
   isEmptyObject 的源码如下:
   isEmptyObject: function( obj ) {
       var name;
       for ( name in obj ) {
           return false;
       }
       return true;
   }
e. 删除自定义数据缓存对象cache[id].data
如果参数pvt不是为true的话,则需要删除自定义的数据缓存对象cache[id].data; 且如果cache[id].data为空对象的话,也需要
通过delete删除掉;如下源码:
// See jQuery.data for more information
if ( !pvt ) {
delete cache[ id ].data;

// Don‘t destroy the parent cache unless the internal data object
// had been the only thing left in it
if ( !isEmptyDataObject( cache[ id ] ) ) {
return;
}
}
f 删除数据缓存对象 cache[id]
如果jQuery.support.deleteExpando为true的话,则支持删除DOM元素上的扩展属性,源码如下:
else if ( jQuery.support.deleteExpando || cache != cache.window ) {
delete cache[ id ];
// When all else fails, null
}
如果为false的话,但是变量cache不是window对象的话,同样执行 delete cache[ id ];
如果jQuery.support.deleteExpando 为false的话,并且变量cache是window对象的话,则执行:cache[ id ] = null;
这是因为不支持删除DOM元素上扩展属性的浏览器,也不支持删除window对象的扩展属性,会抛出异常 对象不支持此操作;

g 删除DOM元素上的扩展属性jQuery.expando属性;如下源码:
if ( isNode ) {
jQuery.cleanData( [ elem ], true );

// Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
}

internalData方法的源码分析

function internalData( elem, name, data, pvt /* Internal Use Only */ ){
        if ( !jQuery.acceptData( elem ) ) {
            return;
        }
        var thisCache, ret,
            internalKey = jQuery.expando,
            getByName = typeof name === "string",

            // We have to handle DOM nodes and JS objects differently because IE6-7
            // can‘t GC object references properly across the DOM-JS boundary
            isNode = elem.nodeType,

            // Only DOM nodes need the global jQuery cache; JS object data is
            // attached directly to the object so GC can occur automatically
            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;

        // Avoid doing any more work than we need to when trying to get data on an
        // object that has no data at all
        if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
            return;
        }

        if ( !id ) {
            // Only DOM nodes need a new unique ID for each element since their data
            // ends up in the global cache
            if ( isNode ) {
                elem[ internalKey ] = id = core_deletedIds.pop() || jQuery.guid++;
            } else {
                id = internalKey;
            }
        }

        if ( !cache[ id ] ) {
            cache[ id ] = {};

            // Avoids exposing jQuery metadata on plain JS objects when the object
            // is serialized using JSON.stringify
            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
        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 );
            }
        }

        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.
        if ( !pvt ) {
            if ( !thisCache.data ) {
                thisCache.data = {};
            }

            thisCache = thisCache.data;
        }

        if ( data !== undefined ) {
            thisCache[ jQuery.camelCase( name ) ] = data;
        }

        // Check for both converted-to-camel and non-converted data property names
        // If a data property was specified
        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;
     }
internalData函数有四个参数,含义分别如下:
@param elem 表示与数据关联的DOM元素.
@param name 表示需要设置或读取的数据名.
@param data 表示需要设置的数据值,任意类型的数据.
@param pvt 表示设置的是内部数据还是自定义数据,如果为true的话,说明是内部数据,否则的话,是自定义数据.
internalData函数源码分析如下:
if ( !jQuery.acceptData( elem ) ) {
return;
}
1. 判断elem是否支持设置数据,如果不支持设置数据的话,直接返回;
2. 定义局部变量;源代码如下:
var  thisCache, ret,
        internalKey = jQuery.expando,
        getByName = typeof name === "string",

        // We have to handle DOM nodes and JS objects differently because IE6-7
        // can‘t GC object references properly across the DOM-JS boundary
        isNode = elem.nodeType,

        // Only DOM nodes need the global jQuery cache; JS object data is
        // attached directly to the object so GC can occur automatically
        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;

各变量的含义如下:
thisCache: 指向数据缓存对象;如果pvt为true,则指向内部数据缓存对象,否则的话,指向与自定义数据缓存对象;
jQuery.expando: 是页面中每个jQuery副本的唯一标识; 它的值为 jQuery + 版本号 + 随机数; 然后去掉非数字字符;源代码如下:
jQuery.extend({
cache: {},
// Unique for each copy of jQuery on the page
// Non-digits removed to match rinlinejQuery
expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" )
});
我们页面包含jquery1.9.1的库下,在chrome控制台运行下可以看到如下:
jQuery.expando
打印:"jQuery19106895217581005935"
isNode: 表示参数elem是否是DOM元素;
cache: 如果是dom元素的话,则把数据对象存储在cache对象上,为了防止javascript和DOM的循环引用,导致不能垃圾回收,因此判断是不是dom
元素,如果是的话,存储在该对象上,如果不是的话,如果是js对象的话,直接存储在javascript对象,垃圾回收机制会自动回收js对象的.
id: 尝试关联id,如果是DOM元素对象的话,关联id是 elem[ jQuery.expando ];否则的话,elem[ internalKey ] && internalKey;

3. if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
return;
}
尝试没有任何数据的对象上读取数据的话,直接返回;
(!id || !cache[id] || (!pvt && !cache[id].data) 的代码含义:
!id的含义是: 如果没有关联id的话,说明没有数据.
!cache[id]的含义是: 如果缓存对象中也没有关联id的话,说明没有数据.
!cache[id].data的含义是: 如果读取的自定义数据的话,没有cache[id].data,也说明么有数据;

getByName && data === undefined的含义是:
如果name是字符串的话,且data是undefined,说明是在读取数据;

4. 如果关联id不存在的话,则给分配一个关联id;代码如下:
var core_deletedIds = [];
if ( !id ) {
// Only DOM nodes need a new unique ID for each element since their data
// ends up in the global cache
if ( isNode ) {
elem[ internalKey ] = id = core_deletedIds.pop() || jQuery.guid++;
} else {
id = internalKey;
}
}
上面代码的含义是: 如果没有关联id,如果是DOM元素节点的话,jQuery.guid默认为1;附加到元素上;否则的话,是javascript对象,则直接
jQuery.expando赋值给id;
比如我们来测试下关联id;测试代码如下:
var body = document.body;
   var $body = $(body);

   // 先移除可以存在的缓存数据
   $body.removeData();

   // 设置自定义数据
   $body.data(‘public-data‘,1);

   // 设置自定义数据
   $.data(body,‘public-data-2‘,2);

   // 设置内部数据
   $.data(body,‘private-data‘,3,true);

   // 打印关联的id
   console.log(‘关联id:‘,$(‘body‘)[0][$.expando]); // 关联id: 1
5. 如果数据缓存对象不存在的话,则初始化空对象 {}; 代码如下: