简介:
$().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内部的执行情况)
- value不存在的话,arguments.length > 1 为false,就使用call执行传入的函数function,将内部的this指向jQuery实例对象,返回执行结果。
- 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的方法,在数据对象中移除指定的属性。
本文详细解析了jQuery中的数据管理功能,重点介绍了$.data方法的使用,包括如何存储、获取和删除数据。文章通过对源码的解读,阐述了内部变量、Data构造函数、Data.prototype以及相关辅助函数的工作原理,帮助读者深入理解jQuery数据管理的实现机制。

4036

被折叠的 条评论
为什么被折叠?



