jQuery.data() 与 jQuery(elem).data()源码解读
之前一直以为 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()源码解读的更多相关文章
- 第二十四课:jQuery.event.remove,dispatch的源码解读
本课还是来讲解一下jQuery是如何实现它的事件系统的.这一课我们先来讲一下jQuery.event.remove的源码解读. remove方法的目的是,根据用户传参,找到事件队列,从里面把匹配的ha ...
- 第二十三课:jQuery.event.add的原理以及源码解读
本课主要来讲解一下jQuery是如何实现它的事件系统的. 我们先来看一个问题: 如果有一个表格有100个tr元素,每个都要绑定mouseover/mouseout事件,改成事件代理的方式,可以节省99 ...
- nodeJS之eventproxy源码解读
1.源码缩影 !(function (name, definition) { var hasDefine = typeof define === 'function', //检查上下文环境是否为AMD ...
- 第二十五课:jQuery.event.trigger的源码解读
本课主要来讲解jQuery.event.trigger的源码解读. trigger = function(event, data, elem, onlyHandlers){ if(elem & ...
- jQuery.Callbacks 源码解读二
一.参数标记 /* * once: 确保回调列表仅只fire一次 * unique: 在执行add操作中,确保回调列表中不存在重复的回调 * stopOnFalse: 当执行回调返回值为false,则 ...
- jQuery源码解读----part 2
分离构造器 通过new操作符构建一个对象,一般经过四步: A.创建一个新对象 B.将构造函数的作用域赋给新对象(所以this就指向了这个新对象) C.执行构造函数中的代码 D.返回这个新对象 最后一点 ...
- jQuery 2.1.4版本的源码分析
jQuery 2.1.4版本的源码分析 jquery中获取元素的源码分析 jQuery.each({// 获取当前元素的父级元素 parent: function(elem) { var parent ...
- jQuery attr() 源码解读
我们知道,$().attr()实质上是内部调用了jQuery.access方法,在调用时jQuery.attr作为回调传入.在通过种种判断(参看jQuery.access()方法)之后,取值和赋值最后 ...
- PyTorch源码解读之torch.utils.data.DataLoader(转)
原文链接 https://blog.csdn.net/u014380165/article/details/79058479 写得特别好!最近正好在学习pytorch,学习一下! PyTorch中数据 ...
- RequireJs 源码解读及思考
写在前面: 最近做的一个项目,用的require和backbone,对两者的使用已经很熟悉了,但是一直都有好奇他们怎么实现的,一直寻思着读读源码.现在项目结束,终于有机会好好研究一下. 本文重要解读r ...
随机推荐
- 【Codeforces】Gym 101156G Non-Attacking Queens 打表
题意 求$n\times n$的棋盘上放$3$个皇后使得互相不攻击的方案数 拓展是$m\times n$棋盘上放$k$皇后,暴力打表找到了公式 OEIS 代码 import java.math.Big ...
- python处理时间汇总
1.将字符串的时间转换为时间戳 方法: a = "2013-10-10 23:40:00" 将其转换为时间数组 import time timeArray = time.strpt ...
- RMAN兼容性、控制文件自动备份、保存时间、备份策略、备份脚本(二)
RMAN 程序的兼容性 RMAN 环境由以下5部分组成:(1) RMAN executable(2) Recovery catalog database(3) Recovery catalog sch ...
- zk 05之:ZooKeeper的配置
ZooKeeper 的功能特性通过 ZooKeeper 配置文件来进行控制管理( zoo.cfg 配置文件). ZooKeeper 这样的设计其实是有它自身的原因的.通过前面对 ZooKeeper 的 ...
- #if _MSC_VER > 1000 #pragma once #endif
#if _MSC_VER > 1000 #pragma once #endif 解释: 这是微软的预编译控制. 在_MSC_VER较小时,它对一些东西的支持与新版不同 _MSC_VER分解如下: ...
- [xdoj1227]Godv的数列(crt+lucas)
解题关键:1001=7*11*13,模数非常小,直接暴力lucas.递归次数几乎为很小的常数.最后用中国剩余定理组合一下即可. 模数很小时,一定记住lucas定理的作用 http://acm.xidi ...
- Http协议-报文
2013的双12即将到来,网上购物是大家所熟悉的.看中小米电视时,可以先下订单然后再付款,电商根据订单将小米电视正确安全的送达给我们.包裹包含电视的基本信息及电视的使用说明书,使我们能够初步的了解它的 ...
- java之数学方法
参考http://how2j.cn/k/number-string/number-string-math/319.html java.lang.Math提供了一些常用的数学运算方法,并且都是以静态方法 ...
- java之异常处理、异常分类、Throwable、自定义异常
参考http://how2j.cn/k/exception/exception-trycatch/336.html 异常处理 try catch 1.将可能抛出FileNotFoundExceptio ...
- Spring入门第十二课
Bean的配置方法 通过工厂方法(静态工厂方法&实例工厂方法),FactoryBean 通过调用静态工厂方法创建Bean 调用静态工厂方法创建Bean是将对象创建的过程封装到静态方法中,当客户 ...