jQuery.Data源码

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

jQuery.data的是jQuery的数据缓存系统。它的主要作用就是为普通对象或者DOM元素添加数据。

1 内部存储原理

这个原理很简单,原本要添加在DOM元素本身的数据,现在被集中的存储在cache集合中。它们之间靠一个从开始的数字键来联系着。这样DOM元素就不会像以前那么笨重了,更不会出现以前那种循环引用而引起的内存泄漏。现在DOM只需要保存好这个数字键值即可。这个属性值被保存在DOM元素的一个属性里,该属性名是由jQuery.expando生成的。

2 Data构造函数

Object.defineProperty( this.cache = {}, 0, {
get: function() {
return {};
}
});

首先来看Object.defineProperty函数,它的作用为:将属性添加到对象,或修改现有属性的特性。那我们先来看下ECMAScript5中的属性。

2.1 ECMAScript5中的属性

ECMAScript5中有两种属性:数据属性和访问器属性。

2.1.1 数据属性:

数据属性包含一个数据值的位置,在这个位置可以读取和写入值。有4种特性用来限制其行为。

①[[configurable]]

能否通过delete删除属性,能否修改属性的特性,能否把属性改为访问器属性。默认为true。

var  obj={name:"abc"};
Object.defineProperty(obj,"name",{
configurable:false
});
console.log(obj.name);
delete obj.name;
console.log(obj.name);
Object.defineProperty(obj,"name",{
configurable:true
});

注意,一旦将 configurable 设为false,就再也设置不回去了。而且还会报错。

②[[enumerable]]

在for-in循环是否能获取到属性。默认为true。ECMAScript规定,由程序员定义的属性的该特性都为true。

③[[writable]]

能否修改属性的值。默认为true。

var  obj={name:"abc"};
Object.defineProperty(obj,"name",{
writable:false
});
console.log(obj.name);
obj.name="111";
console.log(obj.name);

④[[value]]

属性值所在的地方。读取属性值的时候获取的就是它,写属性值的时候也是往这里写。

var  obj={name:"abc"};
console.log(obj.name);
Object.defineProperty(obj,"name",{
value:111
});
console.log(obj.name);

2.1.2访问器属性

访问器属性不包含实际的属性值,它包含两个函数(getter和setter)

①[[get]]

读取属性时调用的函数。默认为undefined

②[[set]]

写入属性时调用的函数。默认为undefined

var obj={};
Object.defineProperties(obj,{
name:{
get:function () {
return obj["_name"];
},
set:function (name) {
if(name != "C#"){
obj["_name"]=name;
}
}
},
label:{
get:function () {
return "你不能改变我!!!";
}
}
});
obj.name="JavaScript";
console.log(obj.name);
obj.label="我偏要改变你!!!";
console.log(obj.label);

上面给cache的属性0,只给了get属性。所以不能为该属性赋值。只能获取。

this.expando = jQuery.expando + Math.random();

这个expando就是用来为DOM元素或者对象存储在cache中的键的。即它将作为DOM元素的一个属性被存储这。

红色画框内的属性名就是由上面的语句生成的,指明div1的属性存储在cache的1属性中。

Data.uid = 1;

该属性表示cache的属性将从1开始自增。因为0已经被这个冻结的空JSON占用了,所以从1开始。我们下次再为某DOM元素添加属性时,它将被保存在cache的2属性中。

3 允许的添加属性的元素

Data.accepts = function( owner ) {
return owner.nodeType ?
owner.nodeType === 1 || owner.nodeType === 9 : true;
};

这段代码写的非常干练。表示如果owner是DOM元素则只有ELEMENT_NODE和DOCUMENT_NODE两种元素能添加属性,如果owner是一个对象,则都可以添加属性。

4 原型属性

4.1 key: function( owner ) {

if ( !Data.accepts( owner ) ) {
return 0;
}

如果owner不能被添加data,则返回cache的第0个元素。

unlock = owner[ this.expando ];

这里获取的就是

如果能从owner中找到这个属性,则说明以前为它添加过值,也就是说,它已经拥有了一个在cache中的key。

if ( !unlock ) {
unlock = Data.uid++;
try {
descriptor[ this.expando ] = { value: unlock};
Object.defineProperties( owner, descriptor );
} catch ( e ) {
descriptor[ this.expando ] = unlock;
jQuery.extend( owner, descriptor );
}
}

如果找不到,则说明是第一次为owner添加属性,则要创建一个key。这个key就是在Data的uid的基础上加1.Data的uid是一个静态属性。这样就能记录前一个元素的key是多少,这次的key又应该是多少。try块里面为owner(即DOM元素或者对象)添加它在cache中的索引。这里会出现兼容性的问题。在Android系统<4时会出现安全问题,因此jQuery使用extend静态方法将descriptor扩展到owner上面。

if ( !this.cache[ unlock ] ) {
this.cache[ unlock ] = {};
}

这里为owner对应在cache中的key赋值一个Object。

这个的key方法的作用就是为DOM元素创建属性,为cache中对应的key赋值(空对象)。

key返回的是这个unlock,即owner对象在cache中对应的key。

4.2 set: function( owner, data, value ) {

unlock = this.key( owner ),
cache = this.cache[ unlock ];

第一句不用解释,第二句或者cachekey值。以上图所示,则这里的cache目前还是{},因为在此之前还没有为div1添加过属性。

if ( typeof data === "string" ) {
cache[ data ] = value;

我们下面的代码走的就是这里:

$("#div1").data("name","div1");

但是我们有很多属性要设置的时候,

$("#div1").data({name:"div1",from:"0",to:"100"});

jQuery的处理方式是这样的:

// Handle: [ owner, { properties } ] args
} else {
// Fresh assignments by object are shallow copied
if ( jQuery.isEmptyObject( cache ) ) {
jQuery.extend( this.cache[ unlock ], data );
// Otherwise, copy the properties one-by-one to the cache object
} else {
for ( prop in data ) {
cache[ prop ] = data[ prop ];
}
}
}

其实这里个人以为不用再判断了,因为extend里面也是用for循环将data的属性扩展到cache中的。

4.3 get: function( owner, key ) {

var cache = this.cache[ this.key( owner ) ];
return key === undefined ? cache : cache[ key ];

获取属性,代码非常简单。如果key不存在则返回cache对象。

4.4 access: function( owner, key, value ) {

这里对get和set方法的统一访问。

4.5 remove: function( owner, key ) {

unlock = this.key( owner ),
cache = this.cache[ unlock ];

获取owner的在cache中的数据对象,this.key返回的是owner在cache中的key。

if ( key === undefined ) {
this.cache[ unlock ] = {};

如果不指定要删除那个属性的话,jQuery会删除owner所有的数据属性。

否则再判断key是不是数组,

if ( jQuery.isArray( key ) ) {
name = key.concat( key.map( jQuery.camelCase ) );
}

是数组的话,将key数组和用key的每一项转驼峰后的数组合并,即:

$("#div1").remove(["one_key","two_key"]);

经过上面的代码,key为变为["one_key","two_key","oneKey","twoKey"]。后面会将这4个属性都删除掉。

那如果key不是数组:

camel = jQuery.camelCase( key );
if ( key in cache ) {
name = [ key, camel ];
} else {
name = camel;
name = name in cache ?
[ name ] : ( name.match( core_rnotwhite ) || [] );
}

同样先输转驼峰,然后判断key是否存在,不存在则判断key的驼峰形式是否存在,后面的正则表达式用于去除驼峰形式前后的空格。

当这些情况过滤完之后,进行删除操作:

i = name.length;
while ( i-- ) {
delete cache[ name[ i ] ];
}

在while循环里面使用delete进行删除。

4.6 hasData: function( owner ) {

cache中是否拥有woner的数据对象。

4.7 discard: function( owner ) {

删除cache中的woner的数据对象。

5 创建两个私有的cache

data_user = new Data();
data_priv = new Data();

所以,我们在jQuery的外面不能直接拿到这个cache。因为它是jQuery的局部变量。data_user供开发人员使用,data_priv供jQuery内部使用。

6 创建对外接口(工具方法和原型方法)

由于Data构造器是jQuery私有的,我们在外面不能访问到,所以前面的那些方法,我们也不能直接访问,jQuery在这里,给我们提供了一些接口。来操作data_user,为DOM元素和Object进行属性操作。工具方法非常简单只是对data_user方法的封装而已。我们主要看下原型方法。

jQuery.fn.extend({
data: function( key, value ) {

设值和取值都会进入上面的方法。

这里有这样一个思想,如果是设值的时候,则给选集中所有的选项设值,如果获取值的时候,只获取第一个选项的值。

if ( key === undefined ) {
if ( this.length ) {
data = data_user.get( elem );
if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
attrs = elem.attributes;
for ( ; i < attrs.length; i++ ) {
name = attrs[ i ].name;
if ( name.indexOf( "data-" ) === 0 ) {
name = jQuery.camelCase( name.slice(5) );
dataAttr( elem, name, data[ name ] );
}
}
data_priv.set( elem, "hasDataAttrs", true );
}
}
return data;
}

在第3行,已经取到cache中elem对应的属性了,下面jQuery由将elem的attributes里的所有属性添加到elem的    。

hasDataAttrs属性是我们自己添加,因为下面这段代码只需要执行一次即可。代码第6行,获取elem所有的属性;

这个NamedNodeMap类型,我们平时很少直接用它,它和NodeList,HTMLCollection一样都是“动态”的。NamedNodeMap集合中的每一项都是Attr类型,Attr对象有3个属性:name,value和specified。

在下面的for循环里面,就检测这个name属性中是否含有"data-"前缀。有的话就去掉它,并且将剩余部分转为驼峰形式。

如果key不存在,则表示获取选集中第一个选项的所有属性值。elem表示第一个选项。

if ( typeof key === "object" ) {
return this.each(function() {
data_user.set( this, key );
});
}

对应这种形式:

$("#div1").remove(["one_key","two_key"]);

jQuery.data的是jQuery的数据缓存系统的更多相关文章

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

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

  2. Memcached 数据缓存系统

    Memcached 数据缓存系统 常用命令及使用:http://www.cnblogs.com/wayne173/p/5652034.html Memcached是一个自由开源的,高性能,分布式内存对 ...

  3. 第十七课:js数据缓存系统的原理

    这一章主要讲的是jQuery的缓存系统的历史发展,以及他自己的框架的缓存系统的实现.都是源码解析. 我就挑几个重点讲下: (1)jQuery的缓存机制的原理 jQuery的缓存机制实现的原理是在元素中 ...

  4. Memcache,Redis,MongoDB(数据缓存系统)方案对比与分析

    mongodb和memcached不是一个范畴内的东西.mongodb是文档型的非关系型数据库,其优势在于查询功能比较强大,能存储海量数据.mongodb和memcached不存在谁替换谁的问题. 和 ...

  5. jQuery数据缓存$.data 的使用以及源码解析

    一.实现原理: 对于DOM元素,通过分配一个唯一的关联id把DOM元素和该DOM元素的数据缓存对象关联起来,关联id被附加到以jQuery.expando的值命名的属性上,数据存储在全局缓存对象jQu ...

  6. jQuery 源码分析(十) 数据缓存模块 data详解

    jQuery的数据缓存模块以一种安全的方式为DOM元素附加任意类型的数据,避免了在JavaScript对象和DOM元素之间出现循环引用,以及由此而导致的内存泄漏. 数据缓存模块为DOM元素和JavaS ...

  7. jQuery 2.0.3 源码分析 数据缓存

    历史背景: jQuery从1.2.3版本引入数据缓存系统,主要的原因就是早期的事件系统 Dean Edwards 的 ddEvent.js代码 带来的问题: 没有一个系统的缓存机制,它把事件的回调都放 ...

  8. jQuery.data() 存储数据

    jQuery.data() 的实现方式 jQuery.data() 的作用是为普通对象或 DOM Element 附加数据. 以下将分三个部分分析事实上现方式: 1. 用name和value为对象附加 ...

  9. jQuery.Data源码

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

随机推荐

  1. Windows 8.1 with Update 镜像下载(增OEM单语言版)

    该系统已有更新的版本,请转至<Windows 8.1 with update 官方最新镜像汇总>下载. 2014年4月9日凌晨,微软向MSDN订阅用户开放了Windows 8.1 with ...

  2. 转:PO BO VO DTO POJO DAO概念及其作用

    J2EE开发中大量的专业缩略语很是让人迷惑,尤其是跟一些高手讨论问题的时候,三分钟就被人家满口的专业术语喷晕了,PO VO BO DTO POJO DAO,一大堆的就来了(听过老罗对这种现象的批判的朋 ...

  3. 我的Android4.3新书即将上市,谢谢大家的支持

    首先感谢清华大学.电子工业.机械工业.人民邮电等各大出版社对本书的肯定.我想说中国的IT业如果没有你们的辛勤工作,是不会发展得这么快的.经过再三权衡,本书将选择人民邮电出版社于近几个月在全国出版发行. ...

  4. Game of Life 解答

    Question According to the Wikipedia's article: "The Game of Life, also known simply as Life, is ...

  5. Populating Next Right Pointers in Each Node II 解答

    Question Follow up for problem "Populating Next Right Pointers in Each Node". What if the ...

  6. vs2008工程配置

    一.添加H文件目录 依次点击“项目——配置属性——C/C++——常规”, 在“附加包含目录”中加入H文件所在的文件夹.(即项目所要用到的所有.h文件目录都要加进去)   二.添加LIB目录 1)依次点 ...

  7. Java I/O 模型的演进

    什么是同步?什么是异步?阻塞和非阻塞又有什么区别?本文先从 Unix 的 I/O 模型讲起,介绍了5种常见的 I/O 模型.而后再引出 Java 的 I/O 模型的演进过程,并用实例说明如何选择合适的 ...

  8. java链接sqlite资料整理

    0.SQLite三种JDBC驱动的区别 摘自http://blog.sina.com.cn/s/blog_654337ca01016x4n.html 在DBeaver中看到SQLite有三种JDBC驱 ...

  9. hdu 5656 CA Loves GCD(dp)

    题目的意思就是: n个数,求n个数所有子集的最大公约数之和. 第一种方法: 枚举子集,求每一种子集的gcd之和,n=1000,复杂度O(2^n). 谁去用? 所以只能优化! 题目中有很重要的一句话! ...

  10. 【多线程】--生产者消费者模式--synchronized版本

    在实现生产者消费者模式之前,我们先了解一下线程的5种状态:被创建.运行.冻结.消亡.阻塞,如下图: 在Jdk1.5发布之前,我们实现生产者消费者模式一般使用synchronized + while循环 ...