之前一直以为 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. Spring笔记04(DI(给属性赋值),自动装配(autowire))

    给不同数据类型注入值: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="h ...

  2. Codeforces 758A. Holiday Of Equality 贪心

    题目大意: 给定一个长为\(n\)序列,每次操作在一个数上+1,求最小的操作次数使所有的数大小相同. 题解: 对这种题无话可说 #include <cstdio> #include < ...

  3. 【Lintcode】120.Word Ladder

    题目: Given two words (start and end), and a dictionary, find the length of shortest transformation se ...

  4. 关闭windows10自动更新

    用windows10的小伙伴们应该都被windows10自动更新这个问题折磨过.那到底要这样禁止windows10的自动更新呢? 百度上有一篇文章写的非常好,并且有配套,大家只要根据步骤操作即可,本人 ...

  5. Enum定义位域, 即可以通过位操作来产生未命名的值

    通过FlagsAttribute可以实现. // A bit field or flag enumeration of harvesting seasons. [Flags] public enum ...

  6. C# 性能分析工具

    http://msdn.microsoft.com/zh-cn/vstudio/aa497289(en-us).aspx Performance This section includes infor ...

  7. Poj_1011_Sticks(剪枝)

    一.Description 乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位.然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度. ...

  8. 二叉树遍历入门 Lebal:research

    解决二叉树遍历的画法 对于二叉树的基本概念,一般学生都知道,但对于二叉树的遍历,在实际运用中可以发现很多问题,这里提供一次性彻底解决这个问题的方法. 二叉树的遍历 二叉树的遍历是指不重复地访问二叉树中 ...

  9. dede问答汉字变星号

    在ask模块里面,question.php中,发现了2行代码 $data['title'] = preg_replace("#{$GLOBALS['cfg_replacestr']}#&qu ...

  10. 干货:SEO长尾关键词优化方法和技巧

    在网站SEO优化上,优化比较成功的网站,根据SEO界前辈的经验结论,网站的总流量主要来源于长尾关键词,占网站总流量的80%.长尾关键词主要分布在网站的文章页,其次就是栏目页title.标签页.专题页等 ...