var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,//匹配json串
rmultiDash = /([A-Z])/g;//匹配大写字母
jQuery.extend({//作用如其名,就是缓存数据,可以对节点或普通对象缓存数据,本对象也可以为jquery程序内部缓存数据
cache: {},
deletedIds: [],
uuid: 0,
expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),//系统内唯一的字串
noData: {//不支持缓存的对象
"embed": true,
"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
"applet": true
},
hasData: function( elem ) {
elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
return !!elem && !isEmptyDataObject( elem );
},
data: function( elem, name, data, pvt /* Internal Use Only */ ) {//作用是为elem对象的name属性设置或赋值
if ( !jQuery.acceptData( elem ) ) {//若元素不接受数据则退出
return;
}
var thisCache, ret,
internalKey = jQuery.expando,//内部唯一性字段,jquery内部唯一,整个执行过程唯 一
getByName = typeof name === "string",//是否是根据属性的名字来取或存数据
isNode = elem.nodeType,//是否是节点
cache = isNode ? jQuery.cache : elem,//节点则保存在jquery.cache里,否则保存在对象里
id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;//如果是节点则从属性里取id, 否则id是jquery内部唯一id
if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {//如果是赋值则data不能为空
return;
}
if ( !id ) {//修正id的值
if ( isNode ) {
elem[ internalKey ] = id = jQuery.deletedIds.pop() || jQuery.guid++;//如果是节点,则为节点添加属性interalKey属性并赋值guid++;
} else {
id = internalKey;//如果不是节点,则直接在对象上添加属性internalKey
}
}
if ( !cache[ id ] ) {//如果cache还没有id这个属性,则赋空值
cache[ id ] = {};
if ( !isNode ) {//如果不是节点,则添加属性toJSON空方法以分辩之
cache[ id ].toJSON = jQuery.noop;
}
}
if ( typeof name === "object" || typeof name === "function" ) {//如果name不是字串而是对象或方法,则为赋值,赋值时要注意保护已存在属性
if ( pvt ) {//如果是内部赋值,则直接在cache[id]上赋值
cache[ id ] = jQuery.extend( cache[ id ], name );
} else {//如果是外部使用,则在cache[id].data上赋值
cache[ id ].data = jQuery.extend( cache[ id ].data, name );
}
}
thisCache = cache[ id ];
if ( !pvt ) {//将当前所操作的缓存对象赋给thisCache
if ( !thisCache.data ) {
thisCache.data = {};
}
thisCache = thisCache.data;
}
if ( data !== undefined ) {//如果data不为空则为赋值,并对name进行驼峰化
thisCache[ jQuery.camelCase( name ) ] = data;
}
if ( getByName ) {//如果name是字符串,则需要取值,未取到则尝试进行驼峰化后再取,如果没有name则输出所有数据
ret = thisCache[ name ];
if ( ret == null ) {
ret = thisCache[ jQuery.camelCase( name ) ];
}
} else {
ret = thisCache;
}
return ret;
},
removeData: function( elem, name, pvt /* Internal Use Only */ ) {//移除数据
if ( !jQuery.acceptData( elem ) ) {//不支持缓存数据则直接退出
return;
}
var thisCache, i, l,
isNode = elem.nodeType,//是否是节点
cache = isNode ? jQuery.cache : elem,//取出缓存对象的父级对象
id = isNode ? elem[ jQuery.expando ] : jQuery.expando;//取出缓存对象对应的id,节点则是当前属性expando的值,否则是expando本身
if ( !cache[ id ] ) {//如果父级对象的id属性为空,则不用再尝试,直接返回
return;
}
if ( name ) {
thisCache = pvt ? cache[ id ] : cache[ id ].data;//找出当前操作对象
if ( thisCache ) {
if ( !jQuery.isArray( name ) ) {//将name转换成数组,,方便后面统一操作
if ( name in thisCache ) {
name = [ name ];
} else {
name = jQuery.camelCase( name );
if ( name in thisCache ) {
name = [ name ];
} else {
name = name.split(" ");
}
}
}
for ( i = 0, l = name.length; i < l; i++ ) {//对数组内所有元素都进行移除操作,
delete thisCache[ name[i] ];
}
if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {//如果此时缓存对象非空,则退出
return;
}
}
}
if ( !pvt ) {//到这里说明name不存在,则全部删除缓存数据
delete cache[ id ].data;//先用delete办法尝试删除
if ( !isEmptyDataObject( cache[ id ] ) ) {
return;
}
}
if ( isNode ) {
jQuery.cleanData( [ elem ], true );//如果是节点则用删除节点属性的办法删除
} else if ( jQuery.support.deleteExpando || cache != cache.window ) {
delete cache[ id ];//如果浏览器支持delete删除对象的属性,或者节点不是frame,则delete删除之
} else {
cache[ id ] = null;//为对象直接赋空值
}
},
_data: function( elem, name, data ) {//内部程序用来保存数据
return jQuery.data( elem, name, data, true );
},
acceptData: function( elem ) {//是否可保存数据,1,不在枚举数组内2, 在枚举数组内但值不为true并且classid与对应值相同
var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
return !noData || noData !== true && elem.getAttribute("classid") === noData;
}
});
jQuery.fn.extend({
data: function( key, value ) {//对外的取赋值方法
var parts, part, attr, name, l,
elem = this[0],//先取出第一个jquery对象
i = 0,
data = null;
if ( key === undefined ) {//如果key不存在,则是取出第一个jquery对象的缓存值
if ( this.length ) {
data = jQuery.data( elem );
if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {//parsedAttrs属性是一个标记,记录是否处理过节点的属性
attr = elem.attributes;
for ( l = attr.length; i < l; i++ ) {
name = attr[i].name;
if ( !name.indexOf( "data-" ) ) {//从jquery.cache中取出数据,写在节点属性上
name = jQuery.camelCase( name.substring(5) );
dataAttr( elem, name, data[ name ] );//
}
}
jQuery._data( elem, "parsedAttrs", true );//标记已处理
}
}
return data;
}//以下为key存在时的处理办法
if ( typeof key === "object" ) {
return this.each(function() {//在所有jquery对象上保存key,因为key是对象
jQuery.data( this, key );
});
}
parts = key.split( ".", 2 );//key可以为.分隔的字符串
parts[1] = parts[1] ? "." + parts[1] : "";
part = parts[1] + "!";
return jQuery.access( this, function( value ) {
if ( value === undefined ) {
data = this.triggerHandler( "getData" + part, [ parts[0] ] );
if ( data === undefined && elem ) {
data = jQuery.data( elem, key );
data = dataAttr( elem, key, data );
}
return data === undefined && parts[1] ?
this.data( parts[0] ) :
data;
}
parts[1] = value;
this.each(function() {
var self = jQuery( this );
self.triggerHandler( "setData" + part, parts );
jQuery.data( this, key, value );
self.triggerHandler( "changeData" + part, parts );
});
}, null, value, arguments.length > 1, null, false );
},
removeData: function( key ) {//移除所有jquery对象的key属性
return this.each(function() {
jQuery.removeData( this, key );
});
}
});
function dataAttr( elem, key, data ) {
if ( data === undefined && elem.nodeType === 1 ) {
var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();//格式为data-ab-cc
data = elem.getAttribute( name );
if ( typeof data === "string" ) {
try {
data = data === "true" ? true ://统一data的值类型
data === "false" ? false :
data === "null" ? null :
+data + "" === data ? +data :
rbrace.test( data ) ? jQuery.parseJSON( data ) ://如果是json格式则解析之,
data;
} catch( e ) {}
jQuery.data( elem, key, data );赋值
} else {
data = undefined;只要data不是字符串则是无效的值
}
}
return data;
}
function isEmptyDataObject( obj ) {//判断obj对象是否是空,忽略空的data属性和toJSON属性
var name;
for ( name in obj ) {
if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
continue;
}
if ( name !== "toJSON" ) {
return false;
}
}
return true;
}