之前一直以为 jQuery(elem).data()是在内部调用了 jQuery.data(),看了代码后发现不是。但是这两个还是需要放在一起看,因为它们内部都使用了jQuery的数据缓存机制。好吧,重点就是data_user对象,跟另一个data_priv是姐妹~~

先来看下jQuery.data():

它调用了data_user.access( elem, name, data );

那么data_user到底是什么鬼呢??

我们看到,它是由Data构造的实例,Data构造器都做了一些什么呢?

console一下data_user,它是这样一个对象:

对象中的cache是一个对象,拥有0和0的访问器,expando是在jQuery.expando的基础上,又加上了一些东西。jQuery.expando又是怎么来的呢?

jQuery+版本号+随机数,当然,版本号和随机数都去了小数点

而data_user是在其上又加了一个随机数。

我们需要重点看的是原型上的方法:

  1. //设置Data的原型
  2. Data.prototype = {
  3. key: function( owner ) {
  4. // We can accept data for non-element nodes in modern browsers,
  5. // but we should not, see #8335.
  6. // Always return the key for a frozen object.
  7. //Data.accepts( owner )返回true的情况:1元素节点2document3对象
  8. if ( !Data.accepts( owner ) ) {
  9. return 0;
  10. }
  11.  
  12. var descriptor = {},
  13. // Check if the owner object already has a cache key
  14. unlock = owner[ this.expando ];//检测当前元素是否有expando,若没有,unlock为undefined
  15.  
  16. // If not, create one
  17. if ( !unlock ) {
  18. unlock = Data.uid++;//
  19. //在owner对象上添加新属性expando,类似"jQuery203029852853389456870.8136399823706597"
  20. //第一次进入时owner是document,第二次是html,所以它们的expando分别是1和2
  21. // Secure it in a non-enumerable, non-writable property
  22. try {
  23. descriptor[ this.expando ] = { value: unlock };
  24. Object.defineProperties( owner, descriptor );
  25.  
  26. // Support: Android < 4
  27. // Fallback to a less secure definition
  28. } catch ( e ) {
  29. descriptor[ this.expando ] = unlock;
  30. jQuery.extend( owner, descriptor );
  31. }
  32.  
  33. }
  34. //如果cache上没有值,初始化为{}
  35. // Ensure the cache object
  36. if ( !this.cache[ unlock ] ) {
  37. this.cache[ unlock ] = {};
  38. }
  39. return unlock;
  40. },
  41. set: function( owner, data, value ) {
  42. var prop,
  43. // There may be an unlock assigned to this node,
  44. // if there is no entry for this "owner", create one inline
  45. // and set the unlock as though an owner entry had always existed
  46. unlock = this.key( owner ),
  47. cache = this.cache[ unlock ];//获取当前的缓存数据cache,每一个cache都是一个对象
  48.  
  49. // Handle: [ owner, key, value ] args
  50. if ( typeof data === "string" ) {//如果传入的参数data是一个字符串,表示只存一个数据
  51. cache[ data ] = value; //直接赋值
  52.  
  53. // Handle: [ owner, { properties } ] args
  54. } else {
  55. // Fresh assignments by object are shallow copied
  56. if ( jQuery.isEmptyObject( cache ) ) {//如果当前的cache是空对象
  57. jQuery.extend( this.cache[ unlock ], data );//扩展对象赋值
  58. // Otherwise, copy the properties one-by-one to the cache object
  59. } else {
  60. for ( prop in data ) {//一条一条赋值
  61. cache[ prop ] = data[ prop ];
  62. }
  63. }
  64. }
  65. return cache;
  66. },
  67. get: function( owner, key ) {
  68. // Either a valid cache is found, or will be created.
  69. // New caches will be created and the unlock returned,
  70. // allowing direct access to the newly created
  71. // empty data object. A valid owner object must be provided.
  72. var cache = this.cache[ this.key( owner ) ];//获取当前owner的缓存数据
  73. //jQuery.event中trigger方法首次和第二次调用
  74. return key === undefined ?
  75. cache : cache[ key ];
  76. },
  77. access: function( owner, key, value ) {
  78.  
  79. var stored;
  80. // In cases where either:
  81. //
  82. // 1. No key was specified
  83. // 2. A string key was specified, but no value provided
  84. //
  85. // Take the "read" path and allow the get method to determine
  86. // which value to return, respectively either:
  87. //
  88. // 1. The entire cache object
  89. // 2. The data stored at the key
  90. //未指定key或者指定了一个字符串key但没有指定值
  91. if ( key === undefined ||
  92. ((key && typeof key === "string") && value === undefined) ) {
  93.  
  94. stored = this.get( owner, key );//获取数据,存储到stored
  95.  
  96. return stored !== undefined ?//如果获取的数据为空,将key转为驼峰后再获取
  97. stored : this.get( owner, jQuery.camelCase(key) );
  98. }
  99.  
  100. // [*]When the key is not a string, or both a key and value
  101. // are specified, set or extend (existing objects) with either:
  102. //
  103. // 1. An object of properties
  104. // 2. A key and value
  105. //
  106. this.set( owner, key, value );
  107.  
  108. // Since the "set" path can have two possible entry points
  109. // return the expected data based on which path was taken[*]
  110. return value !== undefined ? value : key;
  111. },
  112. remove: function( owner, key ) {
  113. var i, name, camel,
  114. unlock = this.key( owner ),
  115. cache = this.cache[ unlock ];
  116.  
  117. if ( key === undefined ) {//如果没有指定key,清空所以数据
  118. this.cache[ unlock ] = {};
  119.  
  120. } else {
  121. // Support array or space separated string of keys
  122. if ( jQuery.isArray( key ) ) {
  123. // If "name" is an array of keys...
  124. // When data is initially created, via ("key", "val") signature,
  125. // keys will be converted to camelCase.
  126. // Since there is no way to tell _how_ a key was added, remove
  127. // both plain key and camelCase key. #12786
  128. // This will only penalize the array argument path.
  129. //将中划线写法改成驼峰,改完后的数组加到原数组后面
  130. //[bar,foo,hello-world]-->[bar,foo,hello-world,bar,foo,helloWorld]
  131. name = key.concat( key.map( jQuery.camelCase ) );
  132. } else {
  133. camel = jQuery.camelCase( key );//如果不是数组就转为驼峰
  134. // Try the string as a key before any manipulation
  135. if ( key in cache ) {//如果key在缓存中
  136. name = [ key, camel ];//[hello-world,helloWorld]
  137. } else {
  138. // If a key with the spaces exists, use it.
  139. // Otherwise, create an array by matching non-whitespace
  140. name = camel;//查找转为驼峰后的key是不是在缓存中
  141. name = name in cache ?//如果在缓存中,返回驼峰key
  142. [ name ] : ( name.match( core_rnotwhite ) || [] );//如果不在,name是驼峰key按空格分隔后的产生的数组,或者返回空数组
  143. }
  144. }
  145.  
  146. i = name.length;
  147. while ( i-- ) {
  148. delete cache[ name[ i ] ];//遍历删除缓存
  149. }
  150. }
  151. },
  152. //根据owner上expando属性中存储的值找到cache的下标,来获取cache中对应位置的数据,检测数据是否空对象
  153. hasData: function( owner ) {
  154. return !jQuery.isEmptyObject(
  155. this.cache[ owner[ this.expando ] ] || {}
  156. );
  157. },
  158. //删除owner在实例上cache中的缓存
  159. discard: function( owner ) {
  160. if ( owner[ this.expando ] ) {
  161. delete this.cache[ owner[ this.expando ] ];
  162. }
  163. }
  164. };

然后来看jQuery(elem).data():

它首先会在自己的user_data里面取值,如果找不到,再通过调用dataAttr方法来取值。而设值是通过data_user.set方法,如果键名中包含"-",它同时支持a-b与aB这两种写法。

  1. data: function( key, value ) {
  2. var attrs, name,
  3. elem = this[ 0 ],//集合中第一个元素
  4. i = 0,
  5. data = null;
  6.  
  7. // Gets all values
  8. if ( key === undefined ) {//取所有值
  9. if ( this.length ) {
  10. data = data_user.get( elem );
  11. //如果是属性节点,且elem没有保存hasDataAttrs的data
  12. if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
  13. attrs = elem.attributes;
  14. for ( ; i < attrs.length; i++ ) {
  15. name = attrs[ i ].name;
  16.  
  17. if ( name.indexOf( "data-" ) === 0 ) {//遍历elem的属性集合,找到其中data-开头的
  18. name = jQuery.camelCase( name.slice(5) );//截取data-后面的字符串作为名称
  19. dataAttr( elem, name, data[ name ] );//调用html5的dataAttr来取值
  20. }
  21. }
  22. data_priv.set( elem, "hasDataAttrs", true );//设值hasDataAttrs为true,表示已经遍历过了
  23. }
  24. }
  25.  
  26. return data;
  27. }
  28.  
  29. // Sets multiple values
  30. if ( typeof key === "object" ) {//设多个值
  31. return this.each(function() {
  32. data_user.set( this, key );//遍历调用data_user.set设值
  33. });
  34. }
  35. //取单个值
  36. return jQuery.access( this, function( value ) {
  37. var data,
  38. camelKey = jQuery.camelCase( key );//转为驼峰表示
  39.  
  40. // The calling jQuery object (element matches) is not empty
  41. // (and therefore has an element appears at this[ 0 ]) and the
  42. // `value` parameter was not undefined. An empty jQuery object
  43. // will result in `undefined` for elem = this[ 0 ] which will
  44. // throw an exception if an attempt to read a data cache is made.
  45. if ( elem && value === undefined ) {
  46. // Attempt to get data from the cache
  47. // with the key as-is
  48. data = data_user.get( elem, key );//通过data_user取值
  49. //如果取到值,直接返回。(取的到值,有两种可能:1原本就是通过data设值的 2、最先不是通过data设值,但是在取到值后把数据存到了data中)
  50. if ( data !== undefined ) {
  51. return data;
  52. }
  53.  
  54. // Attempt to get data from the cache
  55. // with the key camelized
  56. //尝试用驼峰取值
  57. data = data_user.get( elem, camelKey );
  58. if ( data !== undefined ) {
  59. return data;
  60. }
  61. // Attempt to "discover" the data in
  62. // HTML5 custom data-* attrs
  63. //使用h5的data-方式取属性值
  64. data = dataAttr( elem, camelKey, undefined );
  65. if ( data !== undefined ) {
  66. return data;
  67. }
  68.  
  69. // We tried really hard, but the data doesn't exist.
  70. return;
  71. }
  72. //设单个值
  73. // Set the data...
  74. this.each(function() {
  75. // First, attempt to store a copy or reference of any
  76. // data that might've been store with a camelCased key.
  77. var data = data_user.get( this, camelKey );//先存一个驼峰表示拿到的data值
  78.  
  79. // For HTML5 data-* attribute interop, we have to
  80. // store property names with dashes in a camelCase form.
  81. // This might not apply to all properties...*
  82. data_user.set( this, camelKey, value );//设驼峰值
  83.  
  84. // *... In the case of properties that might _actually_
  85. // have dashes, we need to also store a copy of that
  86. // unchanged property.
  87. if ( key.indexOf("-") !== -1 && data !== undefined ) {
  88. data_user.set( this, key, value );//如果key中包含了“-”,另设一个正常值
  89. }
  90. });
  91. }, null, value, arguments.length > 1, null, true );
  92. }

关于dataAttr方法,它实际上还是在取元素的data-xxx属性值,只不过在取完之后,它把结果保存在了data_user中:

  1. function dataAttr( elem, key, data ) {
  2. var name;
  3.  
  4. // If nothing was found internally, try to fetch any
  5. // data from the HTML5 data-* attribute
  6. //如果data未定义且elem是元素节点
  7. if ( data === undefined && elem.nodeType === 1 ) {
  8. //key中只要有一个英文字母是大写,就在该字母前面加“-”,全部转为小写后加前缀'data-'
  9. //如:layUrl=>"data-lay-url";
  10. name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
  11. data = elem.getAttribute( name );//获取属性名
  12.  
  13. if ( typeof data === "string" ) {//如果是字符串
  14. try {
  15. data = data === "true" ? true :
  16. data === "false" ? false :
  17. data === "null" ? null :
  18. // Only convert to a number if it doesn't change the string
  19. +data + "" === data ? +data ://如果data是字符串,就返回字符串
  20. rbrace.test( data ) ? JSON.parse( data ) ://如果是对象或者数组字符串,返回对象或数组
  21. data;//否则返回数值
  22. } catch( e ) {}
  23.  
  24. // Make sure we set the data so it isn't changed later
  25. data_user.set( elem, key, data );//将取到的值保存到data_user中
  26. } else {
  27. data = undefined;
  28. }
  29. }
  30. return data;
  31. }

所以,在使用$.data()的时候,一定要记得它是会把结果缓存起来的,我们通过$.attr('data-xx',xxx)设置的值,的确可以通过$.data('xx')获取到,但是这只在第一次的时候有效,当再次通过$.attr('data-xx',xxx)设新的值,这时候我们发现$.data('xx')总是取到旧的值,这就是因为它在第一次取到数据后就缓存了,之后总是优先读取缓存中的数据。

还有一点,以前我一直以为$.data()与$('xxx').data()是一回事,阅读源码后发现,其实还是不一样的,$.data()与元素的属性无关,所以通过$.attr('data-xx',xxx)设置的值,不能通过$.data()获取。

jQuery.data() 与 jQuery(elem).data()源码解读的更多相关文章

  1. 第二十四课:jQuery.event.remove,dispatch的源码解读

    本课还是来讲解一下jQuery是如何实现它的事件系统的.这一课我们先来讲一下jQuery.event.remove的源码解读. remove方法的目的是,根据用户传参,找到事件队列,从里面把匹配的ha ...

  2. 第二十三课:jQuery.event.add的原理以及源码解读

    本课主要来讲解一下jQuery是如何实现它的事件系统的. 我们先来看一个问题: 如果有一个表格有100个tr元素,每个都要绑定mouseover/mouseout事件,改成事件代理的方式,可以节省99 ...

  3. nodeJS之eventproxy源码解读

    1.源码缩影 !(function (name, definition) { var hasDefine = typeof define === 'function', //检查上下文环境是否为AMD ...

  4. 第二十五课:jQuery.event.trigger的源码解读

    本课主要来讲解jQuery.event.trigger的源码解读. trigger = function(event, data, elem, onlyHandlers){ if(elem & ...

  5. jQuery.Callbacks 源码解读二

    一.参数标记 /* * once: 确保回调列表仅只fire一次 * unique: 在执行add操作中,确保回调列表中不存在重复的回调 * stopOnFalse: 当执行回调返回值为false,则 ...

  6. jQuery源码解读----part 2

    分离构造器 通过new操作符构建一个对象,一般经过四步: A.创建一个新对象 B.将构造函数的作用域赋给新对象(所以this就指向了这个新对象) C.执行构造函数中的代码 D.返回这个新对象 最后一点 ...

  7. jQuery 2.1.4版本的源码分析

    jQuery 2.1.4版本的源码分析 jquery中获取元素的源码分析 jQuery.each({// 获取当前元素的父级元素 parent: function(elem) { var parent ...

  8. jQuery attr() 源码解读

    我们知道,$().attr()实质上是内部调用了jQuery.access方法,在调用时jQuery.attr作为回调传入.在通过种种判断(参看jQuery.access()方法)之后,取值和赋值最后 ...

  9. PyTorch源码解读之torch.utils.data.DataLoader(转)

    原文链接 https://blog.csdn.net/u014380165/article/details/79058479 写得特别好!最近正好在学习pytorch,学习一下! PyTorch中数据 ...

  10. RequireJs 源码解读及思考

    写在前面: 最近做的一个项目,用的require和backbone,对两者的使用已经很熟悉了,但是一直都有好奇他们怎么实现的,一直寻思着读读源码.现在项目结束,终于有机会好好研究一下. 本文重要解读r ...

随机推荐

  1. 【Codeforces】Gym 101156G Non-Attacking Queens 打表

    题意 求$n\times n$的棋盘上放$3$个皇后使得互相不攻击的方案数 拓展是$m\times n$棋盘上放$k$皇后,暴力打表找到了公式 OEIS 代码 import java.math.Big ...

  2. python处理时间汇总

    1.将字符串的时间转换为时间戳 方法: a = "2013-10-10 23:40:00" 将其转换为时间数组 import time timeArray = time.strpt ...

  3. RMAN兼容性、控制文件自动备份、保存时间、备份策略、备份脚本(二)

    RMAN 程序的兼容性 RMAN 环境由以下5部分组成:(1) RMAN executable(2) Recovery catalog database(3) Recovery catalog sch ...

  4. zk 05之:ZooKeeper的配置

    ZooKeeper 的功能特性通过 ZooKeeper 配置文件来进行控制管理( zoo.cfg 配置文件). ZooKeeper 这样的设计其实是有它自身的原因的.通过前面对 ZooKeeper 的 ...

  5. #if _MSC_VER > 1000 #pragma once #endif

    #if _MSC_VER > 1000 #pragma once #endif 解释: 这是微软的预编译控制. 在_MSC_VER较小时,它对一些东西的支持与新版不同 _MSC_VER分解如下: ...

  6. [xdoj1227]Godv的数列(crt+lucas)

    解题关键:1001=7*11*13,模数非常小,直接暴力lucas.递归次数几乎为很小的常数.最后用中国剩余定理组合一下即可. 模数很小时,一定记住lucas定理的作用 http://acm.xidi ...

  7. Http协议-报文

    2013的双12即将到来,网上购物是大家所熟悉的.看中小米电视时,可以先下订单然后再付款,电商根据订单将小米电视正确安全的送达给我们.包裹包含电视的基本信息及电视的使用说明书,使我们能够初步的了解它的 ...

  8. java之数学方法

    参考http://how2j.cn/k/number-string/number-string-math/319.html java.lang.Math提供了一些常用的数学运算方法,并且都是以静态方法 ...

  9. java之异常处理、异常分类、Throwable、自定义异常

    参考http://how2j.cn/k/exception/exception-trycatch/336.html 异常处理 try catch 1.将可能抛出FileNotFoundExceptio ...

  10. Spring入门第十二课

    Bean的配置方法 通过工厂方法(静态工厂方法&实例工厂方法),FactoryBean 通过调用静态工厂方法创建Bean 调用静态工厂方法创建Bean是将对象创建的过程封装到静态方法中,当客户 ...