7.Data相关,数据管理

本文详细解析了jQuery中的数据管理功能,重点介绍了$.data方法的使用,包括如何存储、获取和删除数据。文章通过对源码的解读,阐述了内部变量、Data构造函数、Data.prototype以及相关辅助函数的工作原理,帮助读者深入理解jQuery数据管理的实现机制。

简介:

$().data,用于在jQuery实例对象中存储对象数据。

通过$().data存储、获取数据。

代码大体框架:

var data_user, data_priv, rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
rmultiDash = /([A-Z])/g;

function Data() {
}

Data.uid = 1;

Data.accepts = function(owner) {
}

Data.prototype = {
	key: function(){
	},
	set: function(){
	},
	get: function(){
	},
	access: function(){
	},
	remove: function(){
	},
	hasData: function(){
	},
	discard: function(){
	},
}

data_user = new Data();
data_priv = new Data();

jQuery.extend({
	acceptData:  Data.accepts,
	hasData: function(){
	},
	data: function(){
	},
	removeData: function(){
	},
	_data: function(){
	},
	_removeData: function(){
	},
});

jQuery.fn.extend({
	data: function(){
	},
	removeData: function(){
	}
});

function dataAttr(elem, key, data) {
}

jQuery,data相关功能的代码组成大致如上。

代码解析:

变量:

var data_user, data_priv, rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,rmultiDash = /([A-Z])/g;

先是创建了四个变量,其中data_user是面向用户的数据存储对象;data_priv是内部的数据存储对象;rbrace是正则,用于匹配大括号内或中括号内有任意多的任意字符的字符串;rmultiDash是一个正则,用于匹配单个A到Z的字符。

function Data():

function Data() {
    Object.defineProperty(this.cache = {},
    0, {
        get: function() {
            return {};
        }
    });
    this.expando = jQuery.expando + Math.random();
}

创建Data的构造函数,内部this.cache = {},这个空对象。

使用Object.defineProperty,使得this.cache[0]只能被获取得到{},不能被赋值。

this.expando = jQuery.expando + Math.random(),jQuery.expando是一个属于jQuery对象的标识。这里相当于给Data创建一个标识。

Data.uid = 1;

用于表示添加了数据的节点的标识。

 Data.accepts:

Data.accepts = function(owner) {
    return owner.nodeType ? owner.nodeType === 1 || owner.nodeType === 9 : true;
};

一个函数,属于一个参数,如果参数有属性nodeType的话,判断这个参数是不是节点(document或者普通的dom节点),是的话返回true;没有属性nodeType 的话,直接返回true(这是被是视作是普通对象object)。(感觉这个不是很严谨,而且最新的3.5.1版本也已经改成了新的判断方式 owner.nodeType === 1 || owner.nodeType === 9 || !(+owner.nodeType) )。

Data.prototype:

Data构造函数的原型。

  • key:

key: function(owner) {
	if (!Data.accepts(owner)) {
		return 0;
	}
	var descriptor = {},
	unlock = owner[this.expando];
	if (!unlock) {
		unlock = Data.uid++;
		try {
			descriptor[this.expando] = {
				value: unlock
			};
			Object.defineProperties(owner, descriptor);
		} catch(e) {
			descriptor[this.expando] = unlock;
			jQuery.extend(owner, descriptor);
		}
	}
	if (!this.cache[unlock]) {
		this.cache[unlock] = {};
	}
	return unlock;
},

函数作用,每次为一个新的节点添加一个数据的时候,为节点添加一个标识符。

接收一个owner参数,首先,进行Data.accepts(owner)判断,如果不是DOM节点的话,就退出,直接返回0;

通过的话,创建一个descriptor空对象(用于保存数据的对象),以及unlock,获取owner的属性this.expando(this.expando是Data实例的标识字符串),如果属性不存在的话(还没有创建过数据),就开始将descriptor对象挂载到owner元素节点上。如果存在的话,就直接跳过。

不存在的话,将当前数据的标识赋值给unlock,然后加一。之后给descriptor添加属性,属性名为this.expando,属性内容为unlock。再将descriptor中的属性赋给owner。(其实相当于为owner添加属性this.expando,内容是unlock,最新3.5.1版本已经改过了。给try catch是为了适应不同的浏览器)。

之后确认一下Data实例中cache[unlock]是否存在,如果不存在的话,初始化为{}。

最后,返回unlock,表示添加了数据的节点的标识符。

  • set:

set: function(owner, data, value) {
	var prop,
	unlock = this.key(owner),
	cache = this.cache[unlock];
	if (typeof data === "string") {
		cache[data] = value;
	} else {
		if (jQuery.isEmptyObject(cache)) {
			jQuery.extend(this.cache[unlock], data);
		} else {
			for (prop in data) {
				cache[prop] = data[prop];
			}
		}
	}
	return cache;
},

函数用于添加数据。

先创建变量,prop,用于充当for in遍历的属性;unlock,获取当前的Data.uid值;cache,cache = this.cache[ unlock ],Data实例中的存储数据的数组中,下标为unlock 的数据,即对应节点对应的数据。

如果data是字符串的话,直接在cache中,添加属性data,属性内容为value。

如果不是字符串的话,判断cache是不是空的对象,是的话,将所有data的属性,浅拷贝到cache[unlock]中;反之,就for in遍历data,将data的属性复制到cache中。

 

  • get:

get: function(owner, key) {
	var cache = this.cache[this.key(owner)];
	return key === undefined ? cache: cache[key];
},

输入两个参数,owner和key,ower代表着数据对象挂载的元素,key则是想要获取的对应属性。

先获取ower对应的数据对象,如果属性存在的话,获取对象中的属性内容,反之返回整个对象。

  • access:

access: function(owner, key, value) {
	var stored;
	if (key === undefined || ((key && typeof key === "string") && value === undefined)) {
		stored = this.get(owner, key);
		return stored !== undefined ? stored: this.get(owner, jQuery.camelCase(key));
	}
	this.set(owner, key, value);
	return value !== undefined ? value: key;
},

函数作用,用于获取或者设置挂载在节点上的数据。

输入owner, key, value三个参数。

如果key不存在,或者key存在,但是value不存在,则使用get获取数据,i将结果赋值给store。如果store存在的话,返回结果,不存在的话,将key转为驼峰,重新使用get获取数据返回结果。

如果key存在的话,则使用set,添加或者重新设置属性,并且返回value或者key

  • remove:

remove: function(owner, key) {
	var i, name, camel, unlock = this.key(owner),
	cache = this.cache[unlock];
	if (key === undefined) {
		this.cache[unlock] = {};
	} else {
		if (jQuery.isArray(key)) {
			name = key.concat(key.map(jQuery.camelCase));
		} else {
			camel = jQuery.camelCase(key);
			if (key in cache) {
				name = [key, camel];
			} else {
				name = camel;
				name = name in cache ? [name] : (name.match(core_rnotwhite) || []);
			}
		}
		i = name.length;
		while (i--) {
			delete cache[name[i]];
		}
	}
},

用于删除挂载在指定节点上的指定属性。

输入两个参数,owner和key,数据挂载的元素对象,和需要删除的属性名。

创建变量,i,用来表示要删除的属性的数量;name,表示要删除的属性的数组集合;camel,用于暂存驼峰化的属性名;unlock,节点对应的数据集合在cache中的下标;cache数据集合;

先开始判断key是否存在,如果不存在的话,就将对应的属性全部删除。

如果key存在的话,就判断是否是数组如果是数组的话,就直接将其中元素都驼峰化后的数组和原来的key合并,赋值给name,之后逐个删除属性。

如果不是数组的话,就判断key是否在数据对象中存在,存在的话,就是将key驼峰化,和原来的key一起放入数组name中,之后逐个删除属性。

如果不存在的话,就判断驼峰化的属性是否存在,如果存在的话,给name赋值为只有驼峰化的属性名字符串的数组,反之就是利用正则,匹配分割空格后的数组或者空数组,之后逐个删除属性。

  • hasData:

hasData: function(owner) {
    return ! jQuery.isEmptyObject(this.cache[owner[this.expando]] || {});
},

用来判断节点是否挂载了数据。

  • discard:

discard: function(owner) {
	if (owner[this.expando]) {
		delete this.cache[owner[this.expando]];
	}
}

用于删除挂载在节点上的的对象数据。

function dataAttr(elem, key, data):

function dataAttr(elem, key, data) {
	var name;
	if (data === undefined && elem.nodeType === 1) {
		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:
				+ data + "" === data ? +data: rbrace.test(data) ? JSON.parse(data) : data;
			} catch(e) {}
			data_user.set(elem, key, data);
		} else {
			data = undefined;
		}
	}
	return data;
}

输入三个参数,elem,key,data。内部再创建一个变量name。

如果data不存在且elem的属性nodeType是1的话(dom节点),name赋值为字符串'data-'加上key关键字大写字母前面加上-之后转化为小写(例如,Aaa转化为-aaa)的字符串;data赋值为elem中,属性名为name的属性内容。data赋值之后如果是字符串(getAttribute获取的值,只要存在,就都会是字符串),根据data的字符串类型,再次为data赋值(true字符串赋值为true,false字符串赋值为false,null字符串转化为null,数字字符串转化为数字json字符串转化为json对象,其他的字符串类型不变)。之后再使用data_user实例对象的set方法,为元素挂载数据对象。

如果data存在,或者elem不为节点的话,data赋值为undefined。

最后返回data。

jQuery.extend:

为jQuery添加一些data相关的方法。

  • acceptData:

acceptData: Data.accepts,

直接复制Data的方法accepts,判断接受的参数owner是不是节点。

  • hasData:

hasData: function( elem ) {
	return data_user.hasData( elem ) || data_priv.hasData( elem );
},

调用data_user、data_priv实例对象继承的方法hasData,只要有一个实例对象中存在挂载在节点上的数据对象,就返回true。

  • data:

data: function( elem, name, data ) {
	return data_user.access( elem, name, data );
},

使用data_user实例继承的access方法,进行数据操作。

  • removeData:

removeData: function( elem, name ) {
	data_user.remove( elem, name );
},

使用data_user实例继承的access方法,进行数据的移除。

  • _data:

_data: function( elem, name, data ) {
	return data_priv.access( elem, name, data );
},

使用data_priv实例继承的access方法,进行数据的移除。

  • _removeData:

_data: function( elem, name, data ) {
	data_priv.remove( elem, name );
},

使用data_priv实例继承的access方法,进行数据的移除。

jQuery.fn.extend:

jQuery实例上可以直接调用的方法。

  • data:

data: function( key, value ) {
	var attrs, name,
		elem = this[ 0 ],
		i = 0,
		data = null;
	if ( key === undefined ) {
		if ( this.length ) {
			data = data_user.get( elem );
			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 ) {
						name = jQuery.camelCase( name.slice(5) );
						dataAttr( elem, name, data[ name ] );
					}
				}
				data_priv.set( elem, "hasDataAttrs", true );
			}
		}
		return data;
	}
	if ( typeof key === "object" ) {
		return this.each(function() {
			data_user.set( this, key );
		});
	}
	return jQuery.access( this, function( value ) {
		var data,
			camelKey = jQuery.camelCase( key );
		if ( elem && value === undefined ) {
			data = data_user.get( elem, key );
			if ( data !== undefined ) {
				return data;
			}
			data = data_user.get( elem, camelKey );
			if ( data !== undefined ) {
				return data;
			}
			data = dataAttr( elem, camelKey, undefined );
			if ( data !== undefined ) {
				return data;
			}
			return;
		}
		this.each(function() {
			var data = data_user.get( this, camelKey );
			data_user.set( this, camelKey, value );
			if ( key.indexOf("-") !== -1 && data !== undefined ) {
				data_user.set( this, key, value );
			}
		});
	}, null, value, arguments.length > 1, null, true );
},

这是jQuery实例中,对数据的操作。输入两个参数key,value。

先创建变量,attrs,表示元素的属性attribute的数据集合;name,表示元素attribute的属性名;elem,是jQuery示例中的第一个节点元素;i,在for循环中使用,用于遍历的数字;data,代表挂载在元素上的数据对象。

先判断,如果key关键字不存在的话,表明要获取全部的数据。继续判断,判断jQuery实例中,是否有length属性(表面存在元素),存在的话,就使用data_user实例的get方法,将data赋值为挂载在元素上的数据对象。

再继续判断,如果elem的属性nodeType是1(元素节点)且data_priv实例中,挂载在元素节点上的数据对象,不存在hasDataAttrs属性的话(表明没有设置过hasDataAttrs属性),就将元素上的attribute数组集合赋值给attrs,然后遍历,将属性名赋值给name,判断,如果属性名开头是data-,就获取data-之后的数据,并将属性名驼峰化,然后,将驼峰化的属性名和内容添加到挂载在元素上的属性对象中,然后,在data_priv实例中,给挂载在元素上的数据对象添加hasDataAttrs属性,内容为true。返回数据data。

案例:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>jqueryTest</title>
</head>
<body>
	<div id='one' data-number='one' data-test-number='1'>1</div>
	<script type="text/javascript" src="jquery-2.0.3 formatting.js"></script>
</body>
</html>

如果key关键字存在的话,且为对象的话,说明,要将对象中数据全部复制到挂载在元素上的数据对象中。使用jQuery.fn.each函数,遍历实现data_user.set(this, key)。

如果关键字存在,且不为对象的话,就调用jQuery.access(涉及到了jQuery.access的内部执行情况),执行属性操作。

由于输入的参数是this(jQuery实例对象,保存dom元素)、function(执行函数)、null、value(传入的参数value,再次作为参数传入)、arguments.length > 1(是否存在value,表示是设置属性还是获取属性)、null、true。

key已经确定不为对象且一定存在了,根据value的存在与否,有两种执行情况。(就是jQuery.access内部的执行情况)

  1. value不存在的话,arguments.length > 1 为false,就使用call执行传入的函数function,将内部的this指向jQuery实例对象,返回执行结果。
  2. value存在的话,就相当于使用call执行传入的函数function,将内部的this指向jQuery实例对象,并且传入参数value。然后返回jQuery实例对象。

最后解析传入的函数:接受一个参数value,

有两种情况,一种是elem存在,value不存在的话,直接调用data_user.get函数,传入elem和key,获取相关属性值。不存在的话, 就将key驼峰化继续获取,再不存在就将节点内部的attribute属性值复制到挂载的数据对象中,继续获取,只要有一个获取到,那就赋值给data,返回data;如果都不存在,就直接返回。

如果value存在的话,就使用data_user.set,设置属性属性名为驼峰化后的结果,属性值为value;如果驼峰化的结果已经存在的话,那就设置原来的关键字为属性名。

  • removeData:

removeData: function( key ) {
	return this.each(function() {
		data_user.remove( this, key );
	});
}

遍历jQuery实例中每个内容是节点的属性,然后使用data_user的remove的方法,在数据对象中移除指定的属性。

7-Data Android Recovery 是由 SharpNight LLC 出品的一款安卓数据恢复软件。7-Data Android Recovery 是 7-Data 文件恢复系列中的一员。大眼仔介绍过很多针对 Android 的数据恢复软件,其实大眼仔一款都没有用过,也就不知道哪款好用了。7-Data Android Recovery 有中文界面,采用向导式提示一步步指引用户进行数据恢复操作,可恢复安卓设备上的图片、视频、音频、文档、电子邮件等。由于是自己动手恢复的,所以不存在隐私泄露问题。 安卓数据恢复工具 7-Data Android Recovery 中文版安卓数据恢复工具 7-Data Android Recovery 中文版 7-Data Android Recovery 中文版功能特点 恢复的照片和图片从Android 故意删除或者丢失,由于格式化SD卡的照片可以轻松恢复数据Android的恢复。它帮助恢复各种格式的图片,包括:JPG,JPEG,GIF,PNG,TIFF,PSD等 恢复从Android的视频和音频文件 影片你珍贵的回忆或很长一段时间收集的音乐/歌曲可以是一个巨大的损失。Android的数据恢复支持恢复视频和音频文件格式包括:MP3,MP4,AVI,mpeg,3GP,WAV,WMA,等 恢复以上照片和视频 除了照片和视频等多媒体文件7能够恢复所有其他文件在各种格式,包括Word文档,档案,电子邮件,数据库,应用程序等数据的Android恢复 从外部SD卡恢复在Android 通常情况下会有两个存储器的Android设备。一个是Android设备的内部记忆体,另一种是外部SD卡。7数据的Android恢复支持恢复文件都回忆。 支持广泛的Android设备 设计compatitable与不同版本的Android操作系统,7数据Android的回收支持恢复从Android手机/平板电脑的数据从一个广泛的Android设备制造商,如:谷歌,HTC,三星,摩托罗拉和其他。 安卓数据恢复软件(7-Data Android Recovery)是一个功能完整的 Android 设备数据恢复软件。该软件通过向导式的界面一步步指引用户进行数据恢复操作。专为 Android 系统所使用的手机和平板电脑而设计的,能够有效的恢复安卓手机上的任意数据,其中包括:恢复安卓手机中的照片、图片、视频、音频、文档、电子邮件和其它文件,软件通过向导式的界面一步步指引用户进行数据恢复操作,是一款非常实用的安卓手机恢复软件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值