jQuerydata

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

大家会如何设计一个缓存呢?

一个简单的Cache

(function(){var __cache ={},

Cache ={

get:function(__name){return __cache[__name] ||undefined;

},

set:function(__name, __value){return (__cache[__name] =__value)

}

};this.Cache =Cache;

})();

alert(Cache.get("name"));//undefined

Cache.set("name", "Bob");

alert(Cache.get("name"));//Bob

但这不是jQuery想要的jQuery要解决的是对应元素的缓存数据。

例如,我们用document.getElementById获得了一个元素element,然后有一个对应的参数value的属性名是key,那 么我们想保存到缓存里,那么我们需要告诉缓存element、key、value才能保存数据,而想要获得这个值,则要告诉缓存element和key, 才能得到value。

所以jQuery的缓存实际上是直接绑定到对象中的。

为什么?因为这样简单啊。

用上面的方法,先要将element转成字符串或者数字对应缓存里的对象,然后再用该对象来缓存不同key的value……这……太……麻……烦……了!!

实际上,由于javascript没有Hash值方法,所以对象转字符串或数字并没有太好的方法,当然绑一个ID在元素上除外。

做一个别人一般不会用的令牌但是绑定在对象上有一个问题,如果属性名用什么呢?

如果这个属性名别人也拿去用就悲剧了,比如我用.cache绑定数据,但是另一个库也有.cache来绑定数据,就……

所以,jQuery做了一个正常情况下别人不会用的令牌。

jQuery.expando = "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" );

replace函数将core_verision中的非数字全部替换掉,所以最后这个令牌是一个jQuery后面加一个随机数,比如:

jQuery20018518865841457738

jQuery.hasDatajQuery.hasData =function( elem ) {

elem = elem.nodeType ?jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];return !!elem && !isEmptyDataObject( elem );

};

从这个函数可以看出,如果elem是DOM Element对象,则数据存在jQuery.cahe中,否则存在存在elem对象中。

jQuery.data & jQuery.removeDatajQuery.data =function( elem, name, data ) {return internalData( elem, name, data,false);

};

jQuery.removeData =function( elem, name ) {return internalRemoveData( elem, name,false);

};

他们分别调用了internalData和internalRemoveData。

注意专用接口jQuery._data和jQuery._removeData传的最后一个值有些不同。

这个后面会说到。

jQuery._data =function( elem, name, data ) {return internalData( elem, name, data,true);

};

jQuery._removeData =function( elem, name ) {return internalRemoveData( elem, name,true);

};

internalData

function internalData( elem, name, data, pvt/*Internal Use Only*/){//判断该对象能不能绑定数据if ( !jQuery.acceptData( elem ) ) {return;

}varthisCache, ret,

internalKey =jQuery.expando,

getByName =typeof name === "string",//由于IE6-7的DOM节点引用的垃圾回收问题,需要分开处理DOM节点和JS对象//真心想吐槽,这不是jQuery 2.0么!!!不是说不支持IE6-8么!!!

isNode =elem.nodeType,//如果是DOM节点,则使用jQuery.cache存储数据,否则使用elem本身

cache = isNode ?jQuery.cache : elem,//得到对象的ID号,如果是DOM节点则是其以令牌为属性名的属性值,否则是令牌

id = isNode ? elem[ internalKey ] : elem[ internalKey ] &&internalKey;//避免为了从一个根本没有数据的对象获取数据而浪费时间if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data ===undefined ) {return;

}//如果没有IDif ( !id ) {//如果是DOM节点,就给他一个IDif( isNode ) {

elem[ internalKey ] = id = core_deletedIds.pop() || jQuery.guid++;//否则以令牌作为其ID

}else{

id =internalKey;

}

}//如果对应ID的缓存不存在if ( !cache[ id ] ) {//初始化缓存

cache[ id ] ={};//避免对JS对象使用JSON.stringify时暴露jQuery的元数据对象,所以给对象添加toJSON方法if ( !isNode ) {

cache[ id ].toJSON =jQuery.noop;

}

}//如果name是对象或者函数if (typeof name === "object" ||typeof name === "function") {//如果是jQuery内部私用数据if( pvt ) {//则将数据保存在指定ID的对应缓存中

cache[ id ] =jQuery.extend( cache[ id ], name );

}else{//否则保存在指定ID对应缓存的data属性中

cache[ id ].data =jQuery.extend( cache[ id ].data, name );

}

}//定位缓存中的数据

thisCache =cache[ id ];//区分内部私用以及公用来避免内部数据和用户定义数据的key重复导致的互相覆盖//还有一个有趣的问题,为什么用户数据在data中,而内部数据直接在缓存对象里,而不是反过来呢?//如果不是内部私用if ( !pvt ) {//如果缓存中没有data属性,则初始化一个if ( !thisCache.data ) {

thisCache.data ={};

}//定位缓存位置

thisCache =thisCache.data;

}//如果data已定义,则是写入操作,写入数据if ( data !==undefined ) {

thisCache[ jQuery.camelCase( name ) ] =data;

}//如果name是字符串,即通过字符串来获取数据if( getByName ) {//首先通过name来获取

ret =thisCache[ name ];//看看上面方法有没有得到数据if ( ret ==null) {//如果没有,则用驼峰式name来获取

ret =thisCache[ jQuery.camelCase( name ) ];

}

}else{//不是则直接将数据传出

ret =thisCache;

}returnret;

}

jQuery 2.0中data的实现依然同1.9版本差不多,当然这也不一定是IE6-7的原因才将DOM节点和JS对象分开处理的,我们知道JS引擎读取DOM数据的过程是较为费时费力的,从这个角度来看,将DOM节点的缓存设计在全局会是个比较快的方案。

这里还有两个有趣的问题:

如果传进去的data是函数,那么到底缓存了什么?为什么用户数据在data中,而内部数据直接在缓存对象里,而不是反过来呢?

internalRemoveData

function internalRemoveData( elem, name, pvt/*For internal use only*/){//判断该对象能不能绑定数据if ( !jQuery.acceptData( elem ) ) {return;

}varthisCache, i, l,

isNode =elem.nodeType,

cache = isNode ?jQuery.cache : elem,

id = isNode ?elem[ jQuery.expando ] : jQuery.expando;//如果缓存对象根本不存在,那么就不用删除了if ( !cache[ id ] ) {return;

}//如果name存在if( name ) {//定位缓存位置

thisCache = pvt ?cache[ id ] : cache[ id ].data;//如果缓存存在if( thisCache ) {//支持以空格分隔的字符串if ( !jQuery.isArray( name ) ) {//try the string as a key before any manipulation//看看字符串name存不存在if ( nameinthisCache ) {//定位要删除的缓存

name =[ name ];//不存在证明传进来的是以空格分隔的字符串或者需要转成驼峰写法

}else{//转成驼峰写法

name =jQuery.camelCase( name );//看看现在对不对if ( nameinthisCache ) {

name =[ name ];//不对证明是以空格分隔的字符串

}else{//以空格分隔字符串

name = name.split(" ");

}

}

}else{//如果是数组,则预处理

name =name.concat( jQuery.map( name, jQuery.camelCase ) );

}//遍历删除for ( i = 0, l = name.length; i < l; i++) {deletethisCache[ name[i] ];

}//如果缓存非空,则退出,证明缓存如果都空了,就要删掉它if ( !( pvt ?isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {return;

}

}

}//如果不是私有的if ( !pvt ) {//删除data属性deletecache[ id ].data;//如果缓存对象并非空的,证明可能还有些私有属性存储了,退出if ( !isEmptyDataObject( cache[ id ] ) ) {return;

}

}//到这里已经所有缓存数据都没有了,可以清理ID之类的东西了//如果是DOM节点if( isNode ) {//清理数据

jQuery.cleanData( [ elem ],true);//看看能不能用delete方法删除//还要判断cache本身是不是window对象,否则会抛错

}elseif ( jQuery.support.deleteExpando || cache !=cache.window ) {deletecache[ id ];//如果不能删除则设为null

}else{

cache[ id ] =null;

}

}

jQuery.fn.data

jQuery.fn.data =function( key, value ) {varattrs, name,

elem =this[0],

i = 0,

data =null;//如果key没有被定义,即要得到所有数据if ( key ===undefined ) {//如果长度不为0if (this.length ) {//用jQuery.data获取第一个元素的数据

data =jQuery.data( elem );//如果元素是节点,对应的内部数据parsedAttrs不存在if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs") ) {//从attrubutes中获取数据

attrs =elem.attributes;//遍历for ( ; i < attrs.length; i++) {

name =attrs[i].name;//看看属性名是不是data-xxx,就是要支持HTML5 data-Attributesif ( !name.indexOf( "data-") ) {//是则数据名为data-后面的字符串

name = jQuery.camelCase( name.slice(5) );//数据值通过daataAttr获取dataAttr( elem, name, data[ name ] );

}

}//保存到对应内部缓存parsedAttrs中

jQuery._data( elem, "parsedAttrs",true);

}

}returndata;

}//如果key是对象,则通过jQuery.data设置多个属性if (typeof key === "object") {returnthis.each(function() {

jQuery.data(this, key );

});

}//否则用access操作链式或不用链式return jQuery.access(this,function( value ) {//如果value没有定义,则是读取操作if ( value ===undefined ) {//如果有第一个元素,则返回对应的数据,否则为空return elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) :null;

}//设置操作调用jQuery.data来赋值this.each(function() {

jQuery.data(this, key, value );

});

},null, value, arguments.length > 1,null,true);

};

这个方法主要是支持了HTML5 data-Attributes。

我们可以发现,实际上jQuery最终也把data-Attributes数据也保存到缓存中,这样是为了不再DOM和JS引擎中频繁读取。

jQuery.fn.removeDatajQuery.fn.removeData =function( key ) {returnthis.each(function() {

jQuery.removeData(this, key );

});

}

这个就很简单法了,只是遍历所有元素使用removeData而已。

dataAttr

functiondataAttr( elem, key, data ) {//从data-*获取数据if ( data === undefined && elem.nodeType === 1) {//预处理name,将驼峰式替换成data-*-*形式var name = "data-" + key.replace( rmultiDash, "-$1").toLowerCase();//尝试获取该attribute的数据

data =elem.getAttribute( name );//如果数据是字符串if (typeof data === "string") {try{//如果数据是"true",则为true

data = data === "true" ?true://如果数据是"false",则为false

data === "false" ?false://如果数据时"null",则为null

data === "null" ?null://将字符串转成数字,再转成字符串看看有没有改变,没改变则证明是数字

+data + "" === data ? +data ://否则测试数据是否是以{}包裹,是则尝试转成对象

rbrace.test( data ) ?jQuery.parseJSON( data ) ://否则就当它是普通字符串data;

}catch( e ) {}//保存数据jQuery.data( elem, key, data );//否则数据未定义

}else{

data =undefined;

}

}returndata;

}

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航