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;
});