网站首页  汉语字词  英语词汇  考试资料  写作素材  旧版资料

请输入您要查询的考试资料:

 

标题 jQuery中数据缓存$.data的用法及源码完全解析
内容
    这篇文章主要介绍了jQuery中的数据缓存$.data的用法及源码完全解析,深入解读了jQuery对缓存对象的读写和移除的实现,需要的朋友可以参考下
    一、实现原理:
    对于DOM元素,通过分配一个唯一的关联id把DOM元素和该DOM元素的数据缓存对象关联起来,关联id被附加到以jQuery.expando的值命名的属性上,数据存储在全局缓存对象jQuery.cache中。在读取、设置、移除数据时,将通过关联id从全局缓存对象jQuery.cache中找到关联的数据缓存对象,然后在数据缓存对象上执行读取、设置、移除操作。
    对于Javascript对象,数据则直接存储在该Javascript对象的属性jQuery.expando上。在读取、设置、移除数据时,实际上是对Javascript对象的数据缓存对象执行读取、设置、移除操作。
    为了避免jQuery内部使用的数据和用户自定义的数据发生冲突,数据缓存模块把内部数据存储在数据缓存对象上,把自定义数据存储在数据缓存对象的属性data上。
    二、总体结构:
    // 数据缓存 Data
    jQuery.extend({
       // 全局缓存对象
       cache: {},
       // 唯一 id种子
       uuid:0,
       // 页面中每个jQuery副本的唯一标识
       expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
       // 是否有关联的数据
       hasData: function(){},
       // 设置、读取自定数据或内部数据
       data: function(elem, name, data, pvt) {},
       // 移除自定义数据或内部数据
       removeData: function(elem, name, pvt) {},
       // 设置、读取内部数据
       _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){}
    });
    三、$.data(elem, name, data), $.data(elem, name)
    $.data(elem, name, data)的使用方法:
    如果传入参数name, data, 则设置任意类型的数据
    <!doctype html>
    <html lang="en">
    <head>
     <meta charset="utf-8">
     <title>jQuery.data demo</title>
     <style>
     div {
      color: blue;
     }
     span {
      color: red;
     }
     </style>
     <script src="//code.jquery.com/jquery-1.10.2.js"></script>
    </head>
    <body>
    <div>
     The values stored were
     <span></span>
     and
     <span></span>
    </div>
    <script>
    var div = $( "div" )[ 0 ];
    jQuery.data( div, "test", {
     first: 16,
     last: "pizza!"
    });
    $( "span:first" ).text( jQuery.data( div, "test" ).first );
    $( "span:last" ).text( jQuery.data( div, "test" ).last );
    </script>
    </body>
    </html>
    $.data(elem, name)的使用方法:
    如果传入key, 未传入参数data, 则读取并返回指定名称的数据
    <!doctype html>
    <html lang="en">
    <head>
     <meta charset="utf-8">
     <title>jQuery.data demo</title>
     <style>
     div {
      margin: 5px;
      background: yellow;
     }
     button {
      margin: 5px;
      font-size: 14px;
     }
     p {
      margin: 5px;
      color: blue;
     }
     span {
      color: red;
     }
     </style>
     <script src="//code.jquery.com/jquery-1.10.2.js"></script>
    </head>
    <body>
    <div>A div</div>
    <button>Get "blah" from the div</button>
    <button>Set "blah" to "hello"</button>
    <button>Set "blah" to 86</button>
    <button>Remove "blah" from the div</button>
    <p>The "blah" value of this div is <span>?</span></p>
    <script>
    $( "button" ).click( function() {
     var value,
      div = $( "div" )[ 0 ];
     switch ( $( "button" ).index( this ) ) {
     case 0 :
      value = jQuery.data( div, "blah" );
      break;
     case 1 :
      jQuery.data( div, "blah", "hello" );
      value = "Stored!";
      break;
     case 2 :
      jQuery.data( div, "blah", 86 );
      value = "Stored!";
      break;
     case 3 :
      jQuery.removeData( div, "blah" );
      value = "Removed!";
      break;
     }
     $( "span" ).text( "" + value );
    });
    </script>
    </body>
    </html>
    $.data(elem, name, data), $.data(elem, name) 源码解析:
    jQuery.extend({
     // 1. 定义jQuery.data(elem, name, data, pvt)
     data: function( elem, name, data, pvt /* Internal Use Only */ ) {
      // 2. 检查是否可以设置数据
      if ( !jQuery.acceptData( elem ) ) {
       return; // 如果参数elem不支持设置数据,则立即返回
      }
      // 3 定义局部变量
      var privateCache, 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, // elem是否是DOM元素
       // 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, // 如果是DOM元素,为了避免javascript和DOM元素之间循环引用导致的浏览器(IE6/7)垃圾回收机制不起作用,要把数据存储在全局缓存对象jQuery.cache中;对于javascript对象,来及回收机制能够自动发生,不会有内存泄露的问题,因此数据可以查收存储在javascript对象上
       // 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";
      // Avoid doing any more work than we need to when trying to get data on an
      // object that has no data at all
      // 4. 如果是读取数据,但没有数据,则返回
      if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
       return;
       // getByName && data === undefined 如果name是字符串,data是undefined, 说明是在读取数据
       // !id || !cache[id] || (!isEvents && !pvt && !cache[id].data 如果关联id不存在,说明没有数据;如果cache[id]不存在,也说明没有数据;如果是读取自动以数据,但cache[id].data不存在,说明没有自定义数据
      }
      // 5. 如果关联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 ) {
        elem[ internalKey ] = id = ++jQuery.uuid; // 对于DOM元素,jQuery.uuid会自动加1,并附加到DOM元素上
       } else {
        id = internalKey; // 对于javascript对象,关联id就是jQuery.expando
       }
      }
      // 6. 如果数据缓存对象不存在,则初始化为空对象{}
      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; // 对于javascript对象,设置方法toJSON为空函数,以避免在执行JSON.stringify()时暴露缓存数据。如果一个对象定义了方法toJSON(),JSON.stringify()在序列化该对象时会调用这个方法来生成该对象的JSON元素
       }
      }
      // An object can be passed to jQuery.data instead of a key/value pair; this gets
      // shallow copied over onto the existing cache
      // 7. 如果参数name是对象或函数,则批量设置数据
      if ( typeof name === "object" || typeof name === "function" ) {
       if ( pvt ) {
        cache[ id ] = jQuery.extend( cache[ id ], name ); // 对于内部数据,把参数name中的属性合并到cache[id]中
       } else {
        cache[ id ].data = jQuery.extend( cache[ id ].data, name ); // 对于自定义数据,把参数name中的属性合并到cache[id].data中
       }
      }
      // 8. 如果参数data不是undefined, 则设置单个数据
      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.
      if ( !pvt ) {
       if ( !thisCache.data ) {
        thisCache.data = {};
       }
       thisCache = thisCache.data;
      }
      if ( data !== undefined ) {
       thisCache[ jQuery.camelCase( name ) ] = data;
      }
      // Users should not attempt to inspect the internal events object using jQuery.data,
      // it is undocumented and subject to change. But does anyone listen? No.
      // 9. 特殊处理events 
      if ( isEvents && !thisCache[ name ] ) { // 如果参数name是字符串"events",并且未设置过自定义数据"events",则返回事件婚车对象,在其中存储了事件监听函数。
       return privateCache.events;
      }
      // Check for both converted-to-camel and non-converted data property names
      // If a data property was specified
      //10. 如果参数name是字符串,则读取单个数据
      if ( getByName ) {
       // First Try to find as-is property data
       ret = thisCache[ name ]; // 先尝试读取参数name对应的数据
       // Test for null|undefined property data
       if ( ret == null ) { // 如果未取到,则把参数name转换为驼峰式再次尝试读取对应的数据
        // Try to find the camelCased property
        ret = thisCache[ jQuery.camelCase( name ) ];
       }
      } else { // 11. 如果未传入参数name,data,则返回数据缓存对象
       ret = thisCache;
      }
      return ret;
     },
     // For internal use only.
     _data: function( elem, name, data ) {
      return jQuery.data( elem, name, data, true );
     },
    });
    四、.data(key, value), .data(key)
    使用方法:
    $( "body" ).data( "foo", 52 ); // 传入key, value
    $( "body" ).data( "bar", { myType: "test", count: 40 } ); // 传入key, value
    $( "body" ).data( { baz: [ 1, 2, 3 ] } ); // 传入key, value
    $( "body" ).data( "foo" ); // 52 // 传入key
    $( "body" ).data(); // 未传入参数
    HTML5 data attriubutes:
    <div data-role="page" data-last-value="43" data-hidden="true" data-options='{"name":"John"}'></div>
    $( "div" ).data( "role" ) === "page";
    $( "div" ).data( "lastValue" ) === 43;
    $( "div" ).data( "hidden" ) === true;
    $( "div" ).data( "options" ).name === "John";
    .data(key, value), .data(key) 源码解析
    jQuery.fn.extend({ // 1. 定义.data(key, value)
     data: function( key, value ) {
      var parts, attr, name,
       data = null;
      // 2. 未传入参数的情况
      if ( typeof key === "undefined" ) {
       if ( this.length ) { // 如果参数key是undefined, 即参数格式是.data(), 则调用方法jQuery.data(elem, name, data, pvt)获取第一个匹配元素关联的自定义数据缓存对象,并返回。
        data = jQuery.data( this[0] );
        if ( this[0].nodeType === 1 && !jQuery._data( this[0], "parsedAttrs" ) ) {
         attr = this[0].attributes;
         for ( var i = 0, l = attr.length; i < l; i++ ) {
          name = attr[i].name;
          if ( name.indexOf( "data-" ) === 0 ) {
           name = jQuery.camelCase( name.substring(5) );
           dataAttr( this[0], name, data[ name ] );
          }
         }
         jQuery._data( this[0], "parsedAttrs", true );
        }
       }
       return data;
      // 3. 参数key 是对象的情况,即参数格式是.data(key),则遍历匹配元素集合,为每个匹配元素调用方法jQuery.data(elem, name, data,pvt)批量设置数据
      } else if ( typeof key === "object" ) {
       return this.each(function() {
        jQuery.data( this, key );
       });
      }
      // 4. 只传入参数key的情况 如果只传入参数key, 即参数格式是.data(key),则返回第一个匹配元素的指定名称数据
      parts = key.split(".");
      parts[1] = parts[1] ? "." + parts[1] : "";
      if ( value === undefined ) {
       data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
       // Try to fetch any internally stored data first
       if ( data === undefined && this.length ) {
        data = jQuery.data( this[0], key );
        data = dataAttr( this[0], key, data );
       }
       return data === undefined && parts[1] ?
        this.data( parts[0] ) :
        data;
      // 5. 传入参数key和value的情况 即参数格式是.data(key, value),则为每个匹配元素设置任意类型的数据,并触发自定义事件setData, changeData
      } else {
       return this.each(function() {
        var self = jQuery( this ),
         args = [ parts[0], value ];
        self.triggerHandler( "setData" + parts[1] + "!", args );
        jQuery.data( this, key, value );
        self.triggerHandler( "changeData" + parts[1] + "!", args );
       });
      }
     },
     removeData: function( key ) {
      return this.each(function() {
       jQuery.removeData( this, key );
      });
     }
    });
    // 6. 函数dataAttr(elem, key, data)解析HTML5属性data-
    function dataAttr( elem, key, data ) {
     // If nothing was found internally, try to fetch any
     // data from the HTML5 data-* attribute
     // 只有参数data为undefined时,才会解析HTML5属性data-
     if ( data === undefined && elem.nodeType === 1 ) {
      var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
      data = elem.getAttribute( name );
      if ( typeof data === "string" ) {
       try {
        data = data === "true" ? true :
        data === "false" ? false :
        data === "null" ? null :
        jQuery.isNumeric( data ) ? parseFloat( data ) :
         rbrace.test( data ) ? jQuery.parseJSON( data ) :
         data;
       } catch( e ) {}
       // Make sure we set the data so it isn't changed later
       jQuery.data( elem, key, data );
      } else {
       data = undefined;
      }
     }
     return data;
    }
    五、$.removeData(elem, name),.removeData(key)
    使用方法:
    <!doctype html>
    <html lang="en">
    <head>
     <meta charset="utf-8">
     <title>jQuery.removeData demo</title>
     <style>
     div {
      margin: 2px;
      color: blue;
     }
     span {
      color: red;
     }
     </style>
     <script src="//code.jquery.com/jquery-1.10.2.js"></script>
    </head>
    <body>
    <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" ) ); //undefined
    jQuery.data( div, "test1", "VALUE-1" );
    jQuery.data( div, "test2", "VALUE-2" );
    $( "span:eq(1)" ).text( "" + jQuery.data( div, "test1" ) ); // VALUE-1
    jQuery.removeData( div, "test1" );
    $( "span:eq(2)" ).text( "" + jQuery.data( div, "test1" ) ); // undefined
    $( "span:eq(3)" ).text( "" + jQuery.data( div, "test2" ) ); // value2
    </script>
    </body>
    </html>
    <!doctype html>
    <html lang="en">
    <head>
     <meta charset="utf-8">
     <title>removeData demo</title>
     <style>
     div {
      margin: 2px;
      color: blue;
     }
     span {
      color: red;
     }
     </style>
     <script src="//code.jquery.com/jquery-1.10.2.js"></script>
    </head>
    <body>
    <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>
    $( "span:eq(0)" ).text( "" + $( "div" ).data( "test1" ) ); // undefined
    $( "div" ).data( "test1", "VALUE-1" );
    $( "div" ).data( "test2", "VALUE-2" );
    $( "span:eq(1)" ).text( "" + $( "div").data( "test1" ) ); // VALUE-1
    $( "div" ).removeData( "test1" );
    $( "span:eq(2)" ).text( "" + $( "div" ).data( "test1" ) ); // undefined
    $( "span:eq(3)" ).text( "" + $( "div" ).data( "test2" ) ); // VALUE-2
    </script>
    </body>
    </html>
    $.removeData(elem, name),.removeData(key) 源码解析:
    $.extend({
      // jQuery.removeData(elem,name,pvt)用于移除通过jQuery.data()设置的数据
     removeData: function( elem, name, pvt /* Internal Use Only */ ) {
      if ( !jQuery.acceptData( elem ) ) {
       return;
      }
      var thisCache, i, l,
       // Reference to internal data cache key
       internalKey = jQuery.expando,
       isNode = elem.nodeType,
       // See jQuery.data for more information
       cache = isNode ? jQuery.cache : elem,
       // See jQuery.data for more information
       id = isNode ? elem[ internalKey ] : internalKey;
      // If there is already no cache entry for this object, there is no
      // purpose in continuing
      if ( !cache[ id ] ) {
       return;
      }
        // 如果传入参数name, 则移除一个或多个数据
      if ( name ) {
       thisCache = pvt ? cache[ id ] : cache[ id ].data;
       if ( thisCache ) { // 只有数据缓存对象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( " " );
          }
         }
        }
        // 遍历参数name中的数据名,用运算符delete逐个从数据缓存对象thisCache中移除
        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
      // 删除自定义数据缓存对象cache[id].data
      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;
       }
      }
      // Browsers that fail expando deletion also refuse to delete expandos on
      // the window, but it will allow it on all other JS objects; other browsers
      // don't care
      // Ensure that `cache` is not a window object #10080
      // 删除数据缓存对象cache[id]
      if ( jQuery.support.deleteExpando || !cache.setInterval ) {
       delete cache[ id ];
      } else {
       cache[ id ] = null;
      }
      // We destroyed the cache and need to eliminate the expando on the node to avoid
      // false lookups in the cache for entries that no longer exist
      // 删除DOM元素上扩展的jQuery.expando属性
      if ( isNode ) {
       // IE does not allow us to delete expando properties from nodes,
       // nor does it have a removeAttribute function on Document nodes;
       // we must handle all of these cases
       if ( jQuery.support.deleteExpando ) {
        delete elem[ internalKey ];
       } else if ( elem.removeAttribute ) {
        elem.removeAttribute( internalKey );
       } else {
        elem[ internalKey ] = null;
       }
      }
     }
    });
    jQuery.fn.extend({
      removeData: function( key ) {
       return this.each(function() {
        jQuery.removeData( this, key );
       });
      }
    });
    // checks a cache object for emptiness
    function isEmptyDataObject( obj ) {
     for ( var 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;
    }
    六、$.hasData(elem)
    使用方法:
    <!doctype html>
    <html lang="en">
    <head>
     <meta charset="utf-8">
     <title>jQuery.hasData demo</title>
     <script src="//code.jquery.com/jquery-1.10.2.js"></script>
    </head>
    <body>
    <p>Results: </p>
    <script>
    var $p = jQuery( "p" ), p = $p[ 0 ];
    $p.append( jQuery.hasData( p ) + " " ); // false
    $.data( p, "testing", 123 );
    $p.append( jQuery.hasData( p ) + " " ); // true
    $.removeData( p, "testing" );
    $p.append( jQuery.hasData( p ) + " " ); // false
    $p.on( "click", function() {} );
    $p.append( jQuery.hasData( p ) + " " ); // true
    $p.off( "click" );
    $p.append( jQuery.hasData( p ) + " " ); // false
    </script>
    </body>
    </html>
    $.hasData(elem) 源码解析:
    $.extend({
      hasData: function( elem ) {
       elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
       return !!elem && !isEmptyDataObject( elem );
       // 如果关联的数据缓存对象存在,并且含有数据,则返回true, 否则返回false。 这里用两个逻辑非运算符! 把变量elem转换为布尔值
     }
    });
随便看

 

在线学习网考试资料包含高考、自考、专升本考试、人事考试、公务员考试、大学生村官考试、特岗教师招聘考试、事业单位招聘考试、企业人才招聘、银行招聘、教师招聘、农村信用社招聘、各类资格证书考试等各类考试资料。

 

Copyright © 2002-2024 cuapp.net All Rights Reserved
更新时间:2025/5/14 9:24:29