之前一直以为 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是在其上又加了一个随机数。

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

//设置Data的原型
Data.prototype = {
key: function( owner ) {
// We can accept data for non-element nodes in modern browsers,
// but we should not, see #8335.
// Always return the key for a frozen object.
//Data.accepts( owner )返回true的情况:1元素节点2document3对象
if ( !Data.accepts( owner ) ) {
return 0;
} var descriptor = {},
// Check if the owner object already has a cache key
unlock = owner[ this.expando ];//检测当前元素是否有expando,若没有,unlock为undefined // If not, create one
if ( !unlock ) {
unlock = Data.uid++;//
//在owner对象上添加新属性expando,类似"jQuery203029852853389456870.8136399823706597"
//第一次进入时owner是document,第二次是html,所以它们的expando分别是1和2
// Secure it in a non-enumerable, non-writable property
try {
descriptor[ this.expando ] = { value: unlock };
Object.defineProperties( owner, descriptor ); // Support: Android < 4
// Fallback to a less secure definition
} catch ( e ) {
descriptor[ this.expando ] = unlock;
jQuery.extend( owner, descriptor );
} }
//如果cache上没有值,初始化为{}
// Ensure the cache object
if ( !this.cache[ unlock ] ) {
this.cache[ unlock ] = {};
}
return unlock;
},
set: function( owner, data, value ) {
var prop,
// There may be an unlock assigned to this node,
// if there is no entry for this "owner", create one inline
// and set the unlock as though an owner entry had always existed
unlock = this.key( owner ),
cache = this.cache[ unlock ];//获取当前的缓存数据cache,每一个cache都是一个对象 // Handle: [ owner, key, value ] args
if ( typeof data === "string" ) {//如果传入的参数data是一个字符串,表示只存一个数据
cache[ data ] = value; //直接赋值 // Handle: [ owner, { properties } ] args
} else {
// Fresh assignments by object are shallow copied
if ( jQuery.isEmptyObject( cache ) ) {//如果当前的cache是空对象
jQuery.extend( this.cache[ unlock ], data );//扩展对象赋值
// Otherwise, copy the properties one-by-one to the cache object
} else {
for ( prop in data ) {//一条一条赋值
cache[ prop ] = data[ prop ];
}
}
}
return cache;
},
get: function( owner, key ) {
// Either a valid cache is found, or will be created.
// New caches will be created and the unlock returned,
// allowing direct access to the newly created
// empty data object. A valid owner object must be provided.
var cache = this.cache[ this.key( owner ) ];//获取当前owner的缓存数据
//jQuery.event中trigger方法首次和第二次调用
return key === undefined ?
cache : cache[ key ];
},
access: function( owner, key, value ) { var stored;
// In cases where either:
//
// 1. No key was specified
// 2. A string key was specified, but no value provided
//
// Take the "read" path and allow the get method to determine
// which value to return, respectively either:
//
// 1. The entire cache object
// 2. The data stored at the key
//未指定key或者指定了一个字符串key但没有指定值
if ( key === undefined ||
((key && typeof key === "string") && value === undefined) ) { stored = this.get( owner, key );//获取数据,存储到stored return stored !== undefined ?//如果获取的数据为空,将key转为驼峰后再获取
stored : this.get( owner, jQuery.camelCase(key) );
} // [*]When the key is not a string, or both a key and value
// are specified, set or extend (existing objects) with either:
//
// 1. An object of properties
// 2. A key and value
//
this.set( owner, key, value ); // Since the "set" path can have two possible entry points
// return the expected data based on which path was taken[*]
return value !== undefined ? value : key;
},
remove: function( owner, key ) {
var i, name, camel,
unlock = this.key( owner ),
cache = this.cache[ unlock ]; if ( key === undefined ) {//如果没有指定key,清空所以数据
this.cache[ unlock ] = {}; } else {
// Support array or space separated string of keys
if ( jQuery.isArray( key ) ) {
// 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.
//将中划线写法改成驼峰,改完后的数组加到原数组后面
//[bar,foo,hello-world]-->[bar,foo,hello-world,bar,foo,helloWorld]
name = key.concat( key.map( jQuery.camelCase ) );
} else {
camel = jQuery.camelCase( key );//如果不是数组就转为驼峰
// Try the string as a key before any manipulation
if ( key in cache ) {//如果key在缓存中
name = [ key, camel ];//[hello-world,helloWorld]
} else {
// If a key with the spaces exists, use it.
// Otherwise, create an array by matching non-whitespace
name = camel;//查找转为驼峰后的key是不是在缓存中
name = name in cache ?//如果在缓存中,返回驼峰key
[ name ] : ( name.match( core_rnotwhite ) || [] );//如果不在,name是驼峰key按空格分隔后的产生的数组,或者返回空数组
}
} i = name.length;
while ( i-- ) {
delete cache[ name[ i ] ];//遍历删除缓存
}
}
},
//根据owner上expando属性中存储的值找到cache的下标,来获取cache中对应位置的数据,检测数据是否空对象
hasData: function( owner ) {
return !jQuery.isEmptyObject(
this.cache[ owner[ this.expando ] ] || {}
);
},
//删除owner在实例上cache中的缓存
discard: function( owner ) {
if ( owner[ this.expando ] ) {
delete this.cache[ owner[ this.expando ] ];
}
}
};

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

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

    data: function( key, value ) {
var attrs, name,
elem = this[ 0 ],//集合中第一个元素
i = 0,
data = null; // Gets all values
if ( key === undefined ) {//取所有值
if ( this.length ) {
data = data_user.get( elem );
//如果是属性节点,且elem没有保存hasDataAttrs的data
if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
attrs = elem.attributes;
for ( ; i < attrs.length; i++ ) {
name = attrs[ i ].name; if ( name.indexOf( "data-" ) === 0 ) {//遍历elem的属性集合,找到其中data-开头的
name = jQuery.camelCase( name.slice(5) );//截取data-后面的字符串作为名称
dataAttr( elem, name, data[ name ] );//调用html5的dataAttr来取值
}
}
data_priv.set( elem, "hasDataAttrs", true );//设值hasDataAttrs为true,表示已经遍历过了
}
} return data;
} // Sets multiple values
if ( typeof key === "object" ) {//设多个值
return this.each(function() {
data_user.set( this, key );//遍历调用data_user.set设值
});
}
//取单个值
return jQuery.access( this, function( value ) {
var data,
camelKey = jQuery.camelCase( key );//转为驼峰表示 // The calling jQuery object (element matches) is not empty
// (and therefore has an element appears at this[ 0 ]) and the
// `value` parameter was not undefined. An empty jQuery object
// will result in `undefined` for elem = this[ 0 ] which will
// throw an exception if an attempt to read a data cache is made.
if ( elem && value === undefined ) {
// Attempt to get data from the cache
// with the key as-is
data = data_user.get( elem, key );//通过data_user取值
//如果取到值,直接返回。(取的到值,有两种可能:1原本就是通过data设值的 2、最先不是通过data设值,但是在取到值后把数据存到了data中)
if ( data !== undefined ) {
return data;
} // Attempt to get data from the cache
// with the key camelized
//尝试用驼峰取值
data = data_user.get( elem, camelKey );
if ( data !== undefined ) {
return data;
}
// Attempt to "discover" the data in
// HTML5 custom data-* attrs
//使用h5的data-方式取属性值
data = dataAttr( elem, camelKey, undefined );
if ( data !== undefined ) {
return data;
} // We tried really hard, but the data doesn't exist.
return;
}
//设单个值
// Set the data...
this.each(function() {
// First, attempt to store a copy or reference of any
// data that might've been store with a camelCased key.
var data = data_user.get( this, camelKey );//先存一个驼峰表示拿到的data值 // For HTML5 data-* attribute interop, we have to
// store property names with dashes in a camelCase form.
// This might not apply to all properties...*
data_user.set( this, camelKey, value );//设驼峰值 // *... In the case of properties that might _actually_
// have dashes, we need to also store a copy of that
// unchanged property.
if ( key.indexOf("-") !== -1 && data !== undefined ) {
data_user.set( this, key, value );//如果key中包含了“-”,另设一个正常值
}
});
}, null, value, arguments.length > 1, null, true );
}

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

function dataAttr( elem, key, data ) {
var name; // If nothing was found internally, try to fetch any
// data from the HTML5 data-* attribute
//如果data未定义且elem是元素节点
if ( data === undefined && elem.nodeType === 1 ) {
//key中只要有一个英文字母是大写,就在该字母前面加“-”,全部转为小写后加前缀'data-'
//如:layUrl=>"data-lay-url";
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 :
// Only convert to a number if it doesn't change the string
+data + "" === data ? +data ://如果data是字符串,就返回字符串
rbrace.test( data ) ? JSON.parse( data ) ://如果是对象或者数组字符串,返回对象或数组
data;//否则返回数值
} catch( e ) {} // Make sure we set the data so it isn't changed later
data_user.set( elem, key, data );//将取到的值保存到data_user中
} else {
data = undefined;
}
}
return data;
}

所以,在使用$.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. 监听输入框变化(oninput,onpropertychange,onchange)

    oninput,onpropertychange,onchange: oninput是onpropertychange的非IE浏览器版本,支持firefox和opera等浏览器,但有一点不同,它绑定于 ...

  2. Visual Studio 2012简体中文专业版密钥(激活码)

    VS2012 正式版在Beta版的基础上进行了很多改进,尤其是加入了全新的用户界面. VS2012 的硬件需求与VS2010相同,不过由于 Visual Studio 2012 利用了新版 Windo ...

  3. 解决系统存在大量TIME_WAIT状态的连接

    系统存在大量TIME_WAIT状态的连接,通过调整内核参数解决, vi /etc/sysctl.conf 编辑文件,加入以下内容:net.ipv4.tcp_syncookies = 1net.ipv4 ...

  4. TCP/IP--VLSM

    ##########################VLSM################### 实际案例: 192.168.1.0/28 有几个网络号:2的4次方:16个网络号 : 有几个主机号: ...

  5. NOI.AC 31 MST——整数划分相关的图论(生成树、哈希)

    题目:http://noi.ac/problem/31 模拟 kruscal 的建最小生成树的过程,我们应该把树边一条一条加进去:在加下一条之前先把权值在这一条到下一条的之间的那些边都连上.连的时候要 ...

  6. jquery&nbsp;easyui&nbsp;datebox&nbsp;的使用&nbsp;.

    jquery easyui datebox 的使用 . 分类: jquery-easyui2012-10-09 19:07 266人阅读 评论(0) 收藏 举报 目录(?)[+] 看了jquery e ...

  7. ES5.X相关API和技巧汇总

    https://blog.csdn.net/laoyang360/article/details/77412668

  8. Spring入门第十二课

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

  9. matlab新手入门(二)(翻译)

    矩阵和数组 MATLAB是“矩阵实验室”的缩写.虽然其他编程语言大多数一次使用数字,但MATLAB®主要用于整个矩阵和数组.所有MATLAB变量都是多维数组,无论数据类型如何.矩阵是通常用于线性代数的 ...

  10. 牛客多校3 C-Shuffle Cards(rope大法解决数组分块)

    Shuffle Cards 链接:https://www.nowcoder.com/acm/contest/141/C来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 26 ...