Data源码

实现功能:

为文档元素、节点元素、普通对象以this.expando为键添加Data对象,对外接口获取、设置、移除该Data对象,以及判断该Data对象是否有值;

Data对象分为共有和私有两种,共有的Data对象提供外部接口获取和设置该Data对象的内容,私有的Data对象直接在内部调用构建或获取Data对象的方法,(event模块的回调函数队列通过私有的Data对象构建);

节点元素提供便捷的获取、设置、移除方法,元素的data起始的属性快速合并到Data对象中,通过$.fn.extend方法实现。

源码:

data模块外部接口实现,提供$.data|removeData|hasData|_data|_removeData方法,(后两个方法将弃用,其他模块中构建私有data直接调用dataPriv模块),以及$ele.data|removeData方法,对节点元素进行快速操作。

define([
	"./core",
	"./core/access",
	"./data/var/dataPriv",// 创建私有的Data对象
	"./data/var/dataUser"// 创建共有的Data对象
],function(jQuery,access,dataPriv,dataUser){

"use strict";

var rbrace=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,// {}或[]包裹
	rmultiDash=/[A-Z]/g;

// data不等于undefined时,获取data
// data等于undefined时,获取元素elem的data-key属性,并为elem创建Data对象,获取data
function dataAttr(elem,key,data ){
	var name;

	if ( data===undefined && elem.nodeType===1 ){
		name="data-" + key.replace(rmultiDash,"-$&").toLowerCase();
		data=elem.getAttribute(name);

		if ( typeof data==="string" ){
			try{
				data=data==="true" ? true :
					data==="false" ? false :
					data==="null" ? null :
					+data+""===data ? +data :
					rbrace.test(data) ? JSON.parse(data) :
					data;
			}catch(e){}

			dataUser.set(elem,key,data);// 为elem元素创建Data对象
		}else{
			data=undefined;
		}
	}
	return data;
}

jQuery.extend({
	// 判断elem对象(普通对象、元素或文档节点对象),是否有Data对象属性,以this.expando为键
	hasData:function(elem){
		return dataUser.hasData(elem) || dataPriv.hasData(elem);
	},

	// 设置或获取elem的共有Data对象
	data:function(elem,name,data){
		return dataUser.access(elem,name,data);
	},

	// 移除elem的共有Data对象下割属性,或者清空该内存单元
	removeData: function(elem,name){
		dataUser.remove(elem,name);
	},

	// 私有Data对象构建,为达成其私有性,改由模块中直接调用dataPriv函数
	_data:function(elem,name,data){
		return dataPriv.access(elem,name,data);
	},

	_removeData:function(elem,name){
		dataPriv.remove(elem,name);
	}
});

jQuery.fn.extend({
	data:function(key,value){
		var i, name, data,
			elem=this[0],
			attrs=elem && elem.attributes;

		if ( key===undefined ){
			if ( this.length ){
				data=dataUser.get(elem);// 共有Data对象设置值

				if ( elem.nodeType===1 && !dataPriv.get(elem,"hasDataAttrs") ){
					i=attrs.length;
					while ( i-- ){

						// Support: IE 11 only
						// The attrs elements can be null (#14894)
						if ( attrs[i] ){
							name=attrs[i].name;
							if ( name.indexOf("data-")===0 ){
								name=jQuery.camelCase(name.slice(5));

								// 获取elem下Data对象的属性
								// 或者获取元素的data-name属性,并设置elem下Data对象的属性
								dataAttr(elem,name,data[name]);
							}
						}
					}
					// 当元素的data-name属性再度得到修改时,私有Data对象hasDataAttrs为真
					// 怎样将元素的data-name属性下数据并入共有Data对象??
					dataPriv.set(elem,"hasDataAttrs",true);
				}
			}

			return data;
		}

		if ( typeof key==="object" ){
			return this.each( function(){
				dataUser.set(this,key);
			});
		}

		// data模块中使用access函数,以elem、value为参数执行fn函数,获取或设置Data对象
		return access(this,function(value){
			var data;

			if ( elem && value===undefined ){

				data=dataUser.get(elem,key);
				if ( data!==undefined ){
					return data;
				}

				data=dataAttr(elem,key);
				if ( data!==undefined ){
					return data;
				}

				return;
			}

			this.each(function(){
				dataUser.set(this,key,value);
			});
		},null,value,arguments.length>1,null,true);
	},

	// elem下共有Data对象移除属性,或清空内存
	removeData:function(key){
		return this.each( function(){
			dataUser.remove(this,key);
		});
	}
});

return jQuery;
});

只能接受元素节点、文档节点和普通对象,用以在该对象下构建新的Data对象。

define(function(){

"use strict";

// 判断参数owner是否可接受,接受元素节点、文档节点、普通对象,可接受时创建Data对象
return function(owner){

	// Accepts only:
	//  - Node
	//    - Node.ELEMENT_NODE
	//    - Node.DOCUMENT_NODE
	//  - Object
	//    - Any
	return owner.nodeType===1 || owner.nodeType===9 || !(+owner.nodeType);
};
});

私有Data对象。

define([
	"../Data"// 提供Data对象的方法,最终Data对象附着在原始对象的属性中,从而可配置
],function( Data ){
	"use strict";

	return new Data();// 创建Data对象
});

共有Data对象。

define([
	"../Data"
], function( Data ) {
	"use strict";

	return new Data();
});

以this.expando为键创建Data对象,通常只能通过方法获取或设置Data对象。

cache方法获取Data对象,或者在参数是节点元素、文档元素、普通对象的前提下,创建Data对象;set方法以双参数字符串形式或单参数对象形式设置Data对象;get方法获取Data对象或其属性;access方法首参key为undefined或次参value为undefined时,获取Data对象或其属性,否则为设置Data对象下的属性值,返回key或value;remove方法移除Data对象相应的key键(可为数组),当该Data对象为空时清除内存;hasData方法判断Data对象存在且不为空对象。

define([
	"../core",
	"../var/rnotwhite",// 非空白字符
	"./var/acceptData"// 判断参数owner是否可接受,接受元素节点、文档节点、非节点对象
], function(jQuery,rnotwhite,acceptData){

"use strict";

function Data(){
	this.expando=jQuery.expando+Data.uid++;
}

Data.uid=1;

Data.prototype={

	// 判断owner是否可接受Data设置的对象,可接受时以this.expando创建空对象,或者获取this.expando属性
	cache:function(owner){// event模块中使用owner参数为元素jquery对象

		var value=owner[this.expando];// 获取缓存

		if ( !value ){
			value={};

			// We can accept data for non-element nodes in modern browsers,
			// but we should not, see #8335.
			// Always return an empty object.
			if ( acceptData(owner) ){// 判断参数owner是否可接受,接受元素节点、文档节点、非节点对象

				if ( owner.nodeType ){// 节点jquery对象设置this.expando属性为空对象
					owner[this.expando]=value;

				}else{// 其他对象同样设置this.expando属性为空对象,且可配置
					Object.defineProperty(owner,this.expando,{
						value:value,
						configurable:true
					});
				}
			}
		}

		return value;
	},

	// 设置owner[this.expando]对象的属性,双参数字符串形式或单参数对象形式
	set:function(owner,data,value){
		var prop,
			cache=this.cache(owner);

		if ( typeof data==="string" ){
			cache[jQuery.camelCase(data)]=value;
		}else{
			for ( prop in data ){
				cache[jQuery.camelCase(prop)]=data[prop];
			}
		}
		return cache;
	},

	// 获取owner[this.expando]对象或其属性
	get:function(owner,key){
		return key===undefined ? this.cache(owner) : 
			owner[this.expando] && owner[this.expando][jQuery.camelCase(key)];
	},

	// key为undefined时为获取owner[this.expando],否则为设置,返回value或key
	access:function(owner,key,value){
		// 获取
		if ( key===undefined || ( (key && typeof key==="string") && value===undefined ) ){
			return this.get(owner,key);
		}

		// 设置
		this.set(owner,key,value);

		return value!==undefined ? value : key;
	},

	// 移除owner[this.expando]的key属性(可为数组),owner[this.expando]为空时清除内存
	remove:function(owner,key){
		var i,
			cache=owner[this.expando];

		if ( cache===undefined ){
			return;
		}

		if ( key!==undefined ){

			if ( jQuery.isArray(key) ){

				key=key.map(jQuery.camelCase);
			}else{
				key=jQuery.camelCase(key);

				key=key in cache ? [key] : ( key.match(rnotwhite) || [] );
			}

			i=key.length;

			while ( i-- ){
				delete cache[key[i]];
			}
		}

		if ( key===undefined || jQuery.isEmptyObject(cache) ){
			if ( owner.nodeType ){
				owner[this.expando]=undefined;
			}else{
				delete owner[this.expando];
			}
		}
	},

	// 判断owner[this.expando]对象是否存在
	hasData:function(owner){
		var cache=owner[this.expando];
		return cache!==undefined && !jQuery.isEmptyObject(cache);
	}
};

return Data;
});

相关推荐