jQuery源码学习(版本1.11)-data
2015-05-07 02:13
423 查看
概述
本分详细分析jquery-1.11.1.js源码文件行数:3617~3954;代码简介:data块代码是jQuery用于缓存数据的代码,两种缓存方式,第一种是对于element和document节点对象,data将其需要缓存的数据缓存在内部缓存jQuery.cache中,element里只保存对应的编号,跟jQuery.cache关联起来,第二种对于其他对象,则直接保存在对象自身里面,jQuery源码多处地方用到data缓存功能,如后面的事件管理,JQ对象绑定的事件及其回调全部缓存在jQuery.cache中;
下文进行详细代码分析
代码分析
/** * 判断对象是否适合有data缓存 */ jQuery.acceptData = function( elem ) { // 从elem.nodeName根据jQuery.noData获取元素,用于整个函数的最后判断 var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ], nodeType = +elem.nodeType || 1; // nodeType不为1或9则决不能有缓存,即只有element节点和document节点才能有缓存 return nodeType !== 1 && nodeType !== 9 ? false : // noData为空或false则返回true,表明元素可以用缓存data,elem.getAttribute("classid")这项判断用意暂时不理解 !noData || noData !== true && elem.getAttribute("classid") === noData; }; // 判断是否是json格式的正则 var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, // 用于驼峰转"-[a-z]"的正则 rmultiDash = /([A-Z])/g; // 本方法是作用是将elem属性转到内部缓存去管理,并且返回,后面用其缓存数据,也用其获取数据 function dataAttr( elem, key, data ) { // data是缓存对象key属性的引用,如果为undefined则说明需要转换进去管理 if ( data === undefined && elem.nodeType === 1 ) { // 驼峰转成横杠写法 var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); // 获取data-开头的属性值 data = elem.getAttribute( name ); // data必须是string才会缓存,否则不缓存 // 根据获取的string值进行不同处理 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 : rbrace.test( data ) ? jQuery.parseJSON( data ) : data; } catch( e ) {} // 确保缓存成功 jQuery.data( elem, key, data ); } else { data = undefined; } } 返回缓存值 return data; } // 判断缓存对象obj是否为空对象 function isEmptyDataObject( obj ) { var name; for ( name in obj ) { // 外部使用的数据有可能会存在obj.data,因此特别判断obj.data是否为空,为空则要跳过这一个name if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { continue; } // 只有存在name !== "toJSON" ,则说明有缓存数据,函数返回false, if ( name !== "toJSON" ) { return false; } } // for循环当中无法返回false,则说明缓存对象为空,则返回true return true; } function internalData( elem, name, data, pvt /* Internal Use Only */ ) { // 判断elem是否适合使用data缓存 if ( !jQuery.acceptData( elem ) ) { return; } var ret, thisCache, internalKey = jQuery.expando, // 判断是否dom节点,dom节点使用jQuery内部缓存间接关联数据,因为ie6,ie7无法对dom节点进行回收 isNode = elem.nodeType, // 只有dom节点需要是否jQuery内部缓存,js对象直接在其内部缓存即可 cache = isNode ? jQuery.cache : elem, // 同样只对dom节点定义id(1,2,3...),非dom节点统一使用internalKey id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { return; } // id不存在则对其初始化 if ( !id ) { // 只对dom节点有效 if ( isNode ) { id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++; } else { id = internalKey; } } // 如果cache[ id ]为空,则进行初始化 if ( !cache[ id ] ) { cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; } // name是object或function的场景,直接扩展进cache if ( typeof name === "object" || typeof name === "function" ) { // 如果是内部调用,则扩展进cache[ id ],否则扩展进cache[ id ].data,避免数据冲突 if ( pvt ) { cache[ id ] = jQuery.extend( cache[ id ], name ); } else { cache[ id ].data = jQuery.extend( cache[ id ].data, name ); } } thisCache = cache[ id ]; // 如果没有设置pvt,说明是调用外部使用的工具方法data // 则增加一层,把数据保存在cache[ id ].data里面,以免与内部使用的数据冲突 if ( !pvt ) { if ( !thisCache.data ) { thisCache.data = {}; } // 设置以下引用后,后面直接操作thisCache即可 thisCache = thisCache.data; } // 把data加进缓存中,null也是有效值,因此这里使用三等号判断 if ( data !== undefined ) { thisCache[ jQuery.camelCase( name ) ] = data; } // 获取返回值 if ( typeof name === "string" ) { // 先直接获取而不是转驼峰,因为可能就是需要获取指定值 ret = thisCache[ name ]; // 不存在则把name转成驼峰写法获取 if ( ret == null ) { // 返回的ret是name对应的data数据 ret = thisCache[ jQuery.camelCase( name ) ]; } } else { // 返回的ret是整个缓存对象 ret = thisCache; } return ret; } function internalRemoveData( elem, name, pvt ) { // 判断elem是否适合有缓存 if ( !jQuery.acceptData( elem ) ) { return; } var thisCache, i, isNode = elem.nodeType, // 获取对应的缓存以及对应的id cache = isNode ? jQuery.cache : elem, id = isNode ? elem[ jQuery.expando ] : jQuery.expando; // 如果不存在缓存,则删除动作就是无用的,直接返回没必要继续 if ( !cache[ id ] ) { return; } // name不为空进入删除逻辑 if ( name ) { // 内部使用跟外部使用的是不同层级的缓存,设置好引用thisCache thisCache = pvt ? cache[ id ] : cache[ id ].data; if ( thisCache ) { // 以下if else块的作用是分割name,最终得到一个所有需要删除的key值的数据 // name支持直接传入数组,或使用空格分割的字符串,来达到批删效果 // 判断是否数组或类数组,不是则进入if,是则进入else if ( !jQuery.isArray( name ) ) { // 如果确认是thisCache里的key,直接塞进数组 if ( name in thisCache ) { name = [ name ]; } else { // 转驼峰后再次判断 name = jQuery.camelCase( name ); if ( name in thisCache ) { name = [ name ]; // 前面两个判断都不通过,则分割字符串得到数组 } else { name = name.split(" "); } } } else { // name是数组或类数组,则直接concat转小写之后的结果,得到数组(两倍于原name数组,前一半是原数据,后一半是转小写后的数据) name = name.concat( jQuery.map( name, jQuery.camelCase ) ); } // 循环执行批量删除 i = name.length; while ( i-- ) { delete thisCache[ name[i] ]; } // 再后面的代码是用于消除thisCache缓存的,如果thisCache不为空,则返回,就不执行消除缓存的动作了 if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { return; } } } // 先消除外部使用的缓存 if ( !pvt ) { delete cache[ id ].data; // 如果内部使用的不为空则返回,不执行后续消除cache[ id ]的代码 if ( !isEmptyDataObject( cache[ id ] ) ) { return; } } // 消除该对象缓存 if ( isNode ) { jQuery.cleanData( [ elem ], true ); } else if ( support.deleteExpando || cache != cache.window ) { delete cache[ id ]; } else { cache[ id ] = null; } } // 定义数据缓存相关的工具函数或对象 jQuery.extend({ // jQuery的内部缓存对象(拥有多个用途,事件绑定有使用) cache: {}, // noData是用于保存特殊的不适合有缓存的element noData: { "applet ": true, "embed ": true, // ...but Flash objects (which have this classid) *can* handle expandos "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" }, // 判断elem是否有缓存数据 hasData: function( elem ) { // dom则直接调用jQuery.cache,根据elem内部存的id标示去获取对应缓存,非dom则直接从elem身上获取缓存 elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; // 返回boolean值 return !!elem && !isEmptyDataObject( elem ); }, // 缓存数据或者获取数据 data: function( elem, name, data ) { return internalData( elem, name, data ); }, // 删除缓存数据 removeData: function( elem, name ) { return internalRemoveData( elem, name ); }, // 相当于内部使用的data方法 _data: function( elem, name, data ) { return internalData( elem, name, data, true ); }, // 相当于内部使用的removeData方法 _removeData: function( elem, name ) { return internalRemoveData( elem, name, true ); } }); // JQ原型扩展的data,removeData函数 jQuery.fn.extend({ data: function( key, value ) { var i, name, data, // elem设置为JQ对象的第一个数据 elem = this[0], attrs = elem && elem.attributes; // key为undefined说明是获取所有缓存数据 if ( key === undefined ) { // 判断JQ对象的length,我不懂为什么不直接判断elem if ( this.length ) { // 获取对应到缓存 data = jQuery.data( elem ); // 以下分支应该是获取节点内部的属性,一并返回 // parsedAttrs应该是用来判断节点内部的属性是否已经转到内部缓存的标志 if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { i = attrs.length; // 遍历节点的attributes while ( i-- ) { if ( attrs[ i ] ) { name = attrs[ i ].name; // 如果是以data-为开头的,则需要转到缓存去保存 if ( name.indexOf( "data-" ) === 0 ) { // 驼峰第5位后的字符串 name = jQuery.camelCase( name.slice(5) ); // 调用dataAttr缓存属性 dataAttr( elem, name, data[ name ] ); } } } // 设置parsedAttrs,下次调用就不会再进入上面分支了 jQuery._data( elem, "parsedAttrs", true ); } } // 将获取到的缓存对象返回 return data; } // 如果key是object,则调用each,会对JQ对象里的每一个元素都执行callback // callback里执行jQuery.data( this, key );即将key缓存进每一个元素的缓存中 if ( typeof key === "object" ) { return this.each(function() { jQuery.data( this, key ); }); } return arguments.length > 1 ? // 缓存数据,JQ对象的每个元素都会缓存 // this.each返回的是JQ对象 this.each(function() { jQuery.data( this, key, value ); }) : // 调用dataAttr获取属性,并且将属性缓存起来 elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined; }, // JQ对象的删除缓存的方法,调用each对每一个元素生效 removeData: function( key ) { return this.each(function() { jQuery.removeData( this, key ); }); } });
相关文章推荐
- jQuery源码学习(版本1.11)-整体架构
- jQuery源码学习(版本1.11)-Deferred
- jQuery源码学习(版本1.11)-事件处理-整体架构
- 【代码片-1】 jQuery源码学习(版本1.11)-事件处理-实例函数
- jQuery源码学习(版本1.11)-构造函数
- jQuery源码学习(版本1.11)-queue
- jQuery源码学习(版本1.11)-Callbacks
- jQuery源码学习(版本1.11)-Sizzle代码架构及整体匹配流程
- jQuery源码学习(版本1.11)-事件处理-工具函数jQuery.event
- jQuery源码学习(版本1.11)-事件处理-jQuery事件对象
- JQuery源码学习(2.1.1)之 版本结构 ---- day1
- jquery1.11源码学习——揭秘jquery内幕
- 【菜鸟学习jquery源码】数据缓存与data()
- jquery1.11源码学习——揭秘jquery的extend方法
- jQuery源码学习(版本1.11)-创建jQuery对象
- CYQ.Data 数据框架 V3.5 开源版本发布(源码提供下载)
- jQuery源码学习---简单dom封装(一)
- jquery 源码学习
- linux块设备驱动学习笔记(源码适用高版本内核)
- jQuery源码学习14——源码阅读总结