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

一个简单的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.hasData

jQuery.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.removeData

jQuery.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;
} var thisCache, 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;
} // 如果没有ID
if ( !id ) {
// 如果是DOM节点,就给他一个ID
if ( 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;
} return ret;
}

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

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

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

internalRemoveData

function internalRemoveData( elem, name, pvt /* For 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; // 如果缓存对象根本不存在,那么就不用删除了
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 ( name in thisCache ) {
// 定位要删除的缓存
name = [ name ];
// 不存在证明传进来的是以空格分隔的字符串或者需要转成驼峰写法
} else { //转成驼峰写法
name = jQuery.camelCase( name );
//看看现在对不对
if ( name in thisCache ) {
name = [ name ];
//不对证明是以空格分隔的字符串
} else {
//以空格分隔字符串
name = name.split(" ");
}
}
} else {
// 如果是数组,则预处理
name = name.concat( jQuery.map( name, jQuery.camelCase ) );
} // 遍历删除
for ( i = 0, l = name.length; i < l; i++ ) {
delete thisCache[ name[i] ];
} // 如果缓存非空,则退出,证明缓存如果都空了,就要删掉它
if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
return;
}
}
} // 如果不是私有的
if ( !pvt ) {
// 删除data属性
delete cache[ id ].data; // 如果缓存对象并非空的,证明可能还有些私有属性存储了,退出
if ( !isEmptyDataObject( cache[ id ] ) ) {
return;
}
} // 到这里已经所有缓存数据都没有了,可以清理ID之类的东西了
// 如果是DOM节点
if ( isNode ) {
// 清理数据
jQuery.cleanData( [ elem ], true ); // 看看能不能用delete方法删除
// 还要判断cache本身是不是window对象,否则会抛错
} else if ( jQuery.support.deleteExpando || cache != cache.window ) {
delete cache[ id ]; // 如果不能删除则设为null
} else {
cache[ id ] = null;
}
}

jQuery.fn.data

jQuery.fn.data = function( key, value ) {
var attrs, name,
elem = this[0],
i = 0,
data = null; // 如果key没有被定义,即要得到所有数据
if ( key === undefined ) {
// 如果长度不为0
if ( 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-Attributes
if ( !name.indexOf( "data-" ) ) {
// 是则数据名为data-后面的字符串
name = jQuery.camelCase( name.slice(5) ); // 数据值通过daataAttr获取
dataAttr( elem, name, data[ name ] );
}
}
// 保存到对应内部缓存parsedAttrs中
jQuery._data( elem, "parsedAttrs", true );
}
} return data;
} // 如果key是对象,则通过jQuery.data设置多个属性
if ( typeof key === "object" ) {
return this.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.removeData

jQuery.fn.removeData = function( key ) {
return this.each(function() {
jQuery.removeData( this, key );
});
}

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

dataAttr

function dataAttr( 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;
}
} return data;
}

jQuery data的更多相关文章

  1. jQuery.Data源码

    jQuery.data的是jQuery的数据缓存系统.它的主要作用就是为普通对象或者DOM元素添加数据. 1 内部存储原理 这个原理很简单,原本要添加在DOM元素本身的数据,现在被集中的存储在cach ...

  2. jQuery源码解读 - 数据缓存系统:jQuery.data

    jQuery在1.2后引入jQuery.data(数据缓存系统),主要的作用是让一组自定义的数据可以DOM元素相关联——浅显的说:就是让一个对象和一组数据一对一的关联. 一组和Element相关的数据 ...

  3. 转:jQuery.data

    原文地址:http://www.it165.net/pro/html/201404/11922.html 内存泄露 首先看看什么是内存泄露,这里直接拿来Aaron中的这部分来说明什么是内存泄露,内存泄 ...

  4. JQuery data API实现代码分析

    JQuery data 接口是什么? .data() Store arbitrary data associated with the matched elements or return the v ...

  5. HTML5 自定义属性 data-* 和 jQuery.data 详解

    新的HTML5标准允许你在普通的元素标签里,嵌入类似data-*的属性,来实现一些简单数据的存取.它的数量不受限制,并且也能由javascript动态修改,也支持CSS选择器进行样式设置.这使得dat ...

  6. jquery data方法

    jquery.data()文档:http://api.jquery.com/jQuery.data/ html5有个data-*属性,跟这个功能一样. Note: This is a low-leve ...

  7. jquery data方法取值与js attr取值的区别

    <a data-v="3"></a> jquery data方法的运行机制: 第一次查找dom,使用attributes获取到dom节点值,并将其值存到缓存 ...

  8. jQuery.data的是jQuery的数据缓存系统

    jQuery.Data源码 jQuery.data的是jQuery的数据缓存系统 jQuery.data的是jQuery的数据缓存系统.它的主要作用就是为普通对象或者DOM元素添加数据. 1 内部存储 ...

  9. 读jQuery源码 jQuery.data

    var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, rmultiDash = /([A-Z])/g; function internalData( elem, n ...

随机推荐

  1. Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(3)

    Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(3) 到上一篇为止,拾取一个VBO里的单个图元的问题已经彻底解决了.那么来看下一个问题:一个场景里可能会有多个VBO,此时每个 ...

  2. Mongoose Schemas定义中timestamps选项的妙用

    在Node.js中使用MongoDB少不了Mongoose. 假设有如下Mongoose Schemas的定义: var ItemSchema = new mongoose.Schema({ biz: ...

  3. 搭建LNAMP环境(六)- PHP7源码安装MongoDB和MongoDB拓展

    上一篇:搭建LNAMP环境(五)- PHP7源码安装Redis和Redis拓展 一.安装MongoDB 1.创建mongodb用户组和用户 groupadd mongodb useradd -r -g ...

  4. oracle数据库表的导入导出cmd命令大全

    在实际的项目开发中经常会遇到导入导出oracle数据库中的表,以下是常用的一些cmd命令: 一.数据表的导出 1 将数据库TEST完全导出,用户名system 密码manager 导出到D:daoch ...

  5. Unix及类Unix系统文本编辑器的介绍

    概述 Vim是一个类似于Vi的著名的功能强大.高度可定制的文本编辑器,在Vi的基础上改进和增加了很多特性.VIM是纯粹的自由软件. Vim普遍被推崇为类Vi编辑器中最好的一个,事实上真正的劲敌来自Em ...

  6. php相对于java、js几点不太一样的地方

    1.PHP 变量作用域 在 PHP 中,可以在脚本的任意位置对变量进行声明. 变量的作用域指的是变量能够被引用/使用的那部分脚本. PHP 有三种不同的变量作用域: local(局部) global( ...

  7. Android图片缓存之Glide进阶

    前言: 前面学习了Glide的简单使用(Android图片缓存之初识Glide),今天来学习一下Glide稍微复杂一点的使用. 图片缓存相关博客地址: Android图片缓存之Bitmap详解 And ...

  8. jQuery之empty、remove、detach

    三者都有把元素移除的作用,但细微的差别,造就了它们的使命不同. 最权威的解释当然是jQuery_API咯,下面是API中关于他三儿的部分截取. 一.empty: This method removes ...

  9. beego上传文件

    html代码: <form id="fform" method="POST" enctype="multipart/form-data" ...

  10. WebStorm文件类型关联设置

    无意中创造了一个没有扩展名的文件,我选择了错误的文件类型关联.是js类型的,我却选成了文本,Ws每次编辑类型就成了txt文本,这个问题让我很苦恼,以下是我的解决方案. 错选的弹出框如下: 解决方案如下 ...