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