jQuery源码04 data() : 数据缓存

/*
Implementation Summary 1. Enforce API surface and semantic compatibility with 1.9.x branch
2. Improve the module's maintainability by reducing the storage
paths to a single mechanism.
3. Use the same single mechanism to support "private" and "user" data.
4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
5. Avoid exposing implementation details on user objects (eg. expando properties)
6. Provide a clear path for implementation upgrade to WeakMap in 2014
*/
var data_user, data_priv,
rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
rmultiDash = /([A-Z])/g; function Data() {
// cache = {0:{} },并且这个0属性是不能被修改的。这个0是公用的。
Object.defineProperty( this.cache = {}, 0, {
get: function() {
return {};
}
});
//唯一标识,id
this.expando = jQuery.expando + Math.random();
}
/*
var cache = {
1 : { //同一个元素只有一个id
age : 30,
job : 'it',
'allName' : '妙味课堂'
},
2 : {
age : obj
}
*/
Data.uid = 1;//cache中可以累加的属性1,2,3 Data.accepts = function( owner ) {
// 1是元素,2是document,都是可以在cache中分配id的。
return owner.nodeType ?
owner.nodeType === 1 || owner.nodeType === 9 : true;
}; Data.prototype = {
//分配映射id或者0:空json
key: function( owner ) {//owner是document.body或者$('#div1')对象,
// 不是元素也不是document,就返回0,id就是0,在cache中返回空对象{},defineProperty中给cache定义的空对象。
if ( !Data.accepts( owner ) ) {
return 0;
}
var descriptor = {},
// 获取$('#div1')的属性this.expando的值,
unlock = owner[ this.expando ];
if ( !unlock ) {
unlock = Data.uid++;
try {
/*
descriptor={jQuery203089541586732714850.8840931279098725:{value:1}}
*/
descriptor[ this.expando ] = { value: unlock };
//给owner增加一个属性descriptor:json并且不能改变
Object.defineProperties( owner, descriptor ); // Support: Android < 4
// Fallback to a less secure definition
} catch ( e ) {
//兼容性:通过extend给owner增加{key:value}的键值对,这个可以被修改descriptor={jQuery203089541586732714850.8840931279098725:1}
descriptor[ this.expando ] = unlock;
jQuery.extend( owner, descriptor );
}
}
if ( !this.cache[ unlock ] ) {
this.cache[ unlock ] = {};//cache={1:{}}
}
return unlock;
},
//设置cache的值
set: function( owner, data, value ) {
var prop,
//找到这个元素的id和cache中的json
unlock = this.key( owner ),
cache = this.cache[ unlock ];//cache是某个id的json if ( typeof data === "string" ) {
cache[ data ] = value;//是字符串,就把key value加进去
//是json,$.data(document.body ,{ 'age' : 30 , 'job' : 'it' , 'allName' : '妙味课堂'});
} else {
// Fresh assignments by object are shallow copied
if ( jQuery.isEmptyObject( cache ) ) {
jQuery.extend( this.cache[ unlock ], data );//继承json过去
// Otherwise, copy the properties one-by-one to the cache object
} else {
for ( prop in data ) {
cache[ prop ] = data[ prop ];
}
}
}
return cache;
},
//获取cache中的值
get: function( owner, key ) {
var cache = this.cache[ this.key( owner ) ]; return key === undefined ?
cache : cache[ key ];
},
//set,get合体
access: function( owner, key, value ) {
var stored;
// In cases where either:
//
// 1. No key was specified
// 2. A string key was specified, but no value provided
//
// Take the "read" path and allow the get method to determine
// which value to return, respectively either:
//
// 1. The entire cache object
// 2. The data stored at the key
//
if ( key === undefined ||
((key && typeof key === "string") && value === undefined) ) {
       //没有key全部取出来,有key没有value取key
stored = this.get( owner, key ); return stored !== undefined ?
stored : this.get( owner, jQuery.camelCase(key) );
}
   //有key有value.key是字符串全部覆盖,key是json追加
// [*]When the key is not a string, or both a key and value
// are specified, set or extend (existing objects) with either:
//
// 1. An object of properties
// 2. A key and value
//
this.set( owner, key, value ); // Since the "set" path can have two possible entry points
// return the expected data based on which path was taken[*]
return value !== undefined ? value : key;
}, //移除cache
remove: function( owner, key ) {
var i, name, camel,
unlock = this.key( owner ),
cache = this.cache[ unlock ];//cache是某一个key的cache, if ( key === undefined ) {//不指定key,这个元素所有的都清空。
this.cache[ unlock ] = {}; } else {
// $.removeData(document.body , ['age','job','all-name']);
if ( jQuery.isArray( key ) ) {
//all-name找到allName
name = key.concat( key.map( jQuery.camelCase ) );
} else {
camel = jQuery.camelCase( key );
// Try the string as a key before any manipulation
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 ] ];
}
}
},
hasData: function( owner ) {
return !jQuery.isEmptyObject(
this.cache[ owner[ this.expando ] ] || {}
);
},
discard: function( owner ) {//删除1,2,这个整体
if ( owner[ this.expando ] ) {
delete this.cache[ owner[ this.expando ] ];
}
}
}; // These may be used throughout the jQuery core codebase
data_user = new Data();//cache就是依附在这个对象上
data_priv = new Data(); //对外提供的接口,通过$.直接调用。这个是方法的定义,准备给jQuery类调用的,里面的实现都是调用Data对象的实现。
jQuery.extend({
acceptData: Data.accepts, hasData: function( elem ) {
return data_user.hasData( elem ) || data_priv.hasData( elem );
}, data: function( elem, name, data ) {
return data_user.access( elem, name, data );
}, removeData: function( elem, name ) {
data_user.remove( elem, name );
}, // TODO: Now that all calls to _data and _removeData have been replaced
// with direct calls to data_priv methods, these can be deprecated.
//私有的,内部使用,不对外使用
_data: function( elem, name, data ) {
return data_priv.access( elem, name, data );
}, _removeData: function( elem, name ) {
data_priv.remove( elem, name );
}
}); //对外提供的接口,通过jQuery对象调用。设置一组元素时候是设置所有元素,获取元素只是获取第一个。
jQuery.fn.extend({
data: function( key, value ) {
var attrs, name,
elem = this[ 0 ],//如果是一组div,就只要第一个,
i = 0,
data = null; // Gets all values
if ( key === undefined ) {
if ( this.length ) {//$('#div1')找不到得到
data = data_user.get( elem );
//alert($('#div1').get(0).dataset.miaovAll);//h5特性,data-miaov-all="妙味"
//对h5特性的处理
if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
attrs = elem.attributes;//元素所有属性数组集合[title='123',class='box',data-miaov-all='苗圩',id='div1']
for ( ; i < attrs.length; i++ ) {
name = attrs[ i ].name;//属性的名字 if ( name.indexOf( "data-" ) === 0 ) {
//miaov-all转成miaoAll,js中不要出现横杆
name = jQuery.camelCase( name.slice(5) );
//存入cache
dataAttr( elem, name, data[ name ] );
}
}
data_priv.set( elem, "hasDataAttrs", true );
}
} return data;
} // Sets multiple values$('#div1').data({name:'hello',age:'30'});
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 ); // The calling jQuery object (element matches) is not empty
// (and therefore has an element appears at this[ 0 ]) and the
// `value` parameter was not undefined. An empty jQuery object
// will result in `undefined` for elem = this[ 0 ] which will
// throw an exception if an attempt to read a data cache is made.
if ( elem && value === undefined ) {
// Attempt to get data from the cache
// with the key as-is
data = data_user.get( elem, key );
if ( data !== undefined ) {
return data;
} // Attempt to get data from the cache
// with the key camelized
data = data_user.get( elem, camelKey );
if ( data !== undefined ) {
return data;
} // Attempt to "discover" the data in
// HTML5 custom data-* attrs
data = dataAttr( elem, camelKey, undefined );
if ( data !== undefined ) {
return data;
} // We tried really hard, but the data doesn't exist.
return;
} // Set the data...
this.each(function() {
// First, attempt to store a copy or reference of any
// data that might've been store with a camelCased key.
var data = data_user.get( this, camelKey ); // For HTML5 data-* attribute interop, we have to
// store property names with dashes in a camelCase form.
// This might not apply to all properties...*
data_user.set( this, camelKey, value ); // *... In the case of properties that might _actually_
// have dashes, we need to also store a copy of that
// unchanged property.
if ( key.indexOf("-") !== -1 && data !== undefined ) {
data_user.set( this, key, value );
}
});
}, null, value, arguments.length > 1, null, true );
}, removeData: function( key ) {
return this.each(function() {
data_user.remove( this, key );
});
}
}); function dataAttr( elem, key, data ) {
var name; // If nothing was found internally, try to fetch any
// data from the HTML5 data-* attribute
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 :
// Only convert to a number if it doesn't change the string
+data + "" === data ? +data :
rbrace.test( data ) ? JSON.parse( data ) :
data;
} catch( e ) {} // Make sure we set the data so it isn't changed later
data_user.set( elem, key, data );
} else {
data = undefined;
}
}
return data;
}
上一篇:dp进阶——饥饿的奶牛


下一篇:JS跨域方法及原理