jQuery源码学习(版本1.11)-data

13 篇文章 0 订阅

概述

本分详细分析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 );
		});
	}
});


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值