转:jQuery.data
原文地址:http://www.it165.net/pro/html/201404/11922.html
内存泄露
首先看看什么是内存泄露,这里直接拿来Aaron中的这部分来说明什么是内存泄露,内存泄露的3种情况:
1 循环引用
2 Javascript闭包
3 DOM插入顺序
在这里我们只解释第一种情况,因为jquery的数据缓存就是解决这类的内存泄露的。一个DOM对象被一个Javascript对象引用,与此同时又引用同一个或其它的Javascript对象,这个DOM对象可能会引发内存泄漏。这个DOM对象的引用将不会在脚本停止的时候被垃圾回收器回收。要想破坏循环引用,引用DOM元素的对象或DOM对象的引用需要被赋值为null。
含有DOM对象的循环引用将导致大部分当前主流浏览器内存泄露
第一种:多个对象循环引用
1.
var a=
new
Object;
2.
3.
var b=
new
Object;
4.
5.
a.r=b;
6.
7.
b.r=a;
第二种:循环引用自己
1.
var a=
new
Object;
2.
3.
a.r=a;
循环引用很常见且大部分情况下是无害的,但当参与循环引用的对象中有DOM对象或者ActiveX对象时,循环引用将导致内存泄露。
我们把例子中的任何一个new Object替换成document.getElementById或者document.createElement就会发生内存泄露了。
在实际应用中我们要给我们的DOM添加数据,如果我们给一个DOM添加的数据太多的话,会存在循环引用的风险,例如我们添加的数据恰好引用了这个DOM元素,就会存在内存的泄露。所以jquery使用了数据缓存的机制就解决或者说避免这一问题。
数据缓存
$.cache 是jquery的缓存对象,这个是对象就是一个json,它的结构是这样的
1.
{
'uid1'
: {
// DOM节点1缓存数据,
2.
'name1'
: value1,
3.
'name2'
: value2
4.
},
5.
'uid2'
: {
// DOM节点2缓存数据,
6.
'name1'
: value1,
7.
'name2'
: value2
8.
}
数据缓存的接口是
$.data( element, key, value )
$(selector).data(key,value)
用法
看代码之前,先看看怎么使用jquery的数据缓存。在jquery中,有两个方法可以给对象设置数据,分别是实例方法$().data()和静态方法$.data(),具体的使用过程大家看api就知道了,这里简单介绍下
静态方法$.data()有三个参数,分别是挂在数据的元素,挂载的数据键,挂载数据的值,根据参数的不同,无非就是设置数据,取数据,具体如下
1 $.data( elem, key, value ) 在指定元素上存储/添加任意的数据,处理了循环引用和内存泄漏问题
2 $.data( elem, key ) 返回指定元素上name指定的值
3 $.data( elem ) 返回全部数据
4 $.data( elem,obj ) 在指定的元素上绑定obj01.
var obj = {};
02.
$.data(obj ,
'a'
,
1
);
//普通对象添加数据
03.
console.log($.data(obj,
'a'
));
//1
04.
var dom = $(
'body'
);
//dom添加数据
05.
$.data(dom,
'a'
,
1
)
06.
console.log($.data(dom,
'a'
));
//1
07.
$.data(obj , {
'b'
:
2
});
//两个参数 绑定数据对象
08.
console.log($.data(dom,
'b'
));
//2
09.
console.log($.data(dom));
//1 2
静态方法$().data()有两个参数,挂载的数据键,挂载数据的值
1 $(selector).data( key, value ) 在指定元素上存储/添加任意的数据,处理了循环引用和内存泄漏问题
2 $(selector).data( key ) 返回指定元素上name指定的值
3 $(selector).data(obj ) 在指定的元素上绑定obj
4 $(selector).data() 返回全部数据1.
$(
'body'
).data(
'a'
,
1
);
//添加数据
2.
console.log($(
'body'
).data(
'a'
));
//1
3.
$(
'body'
).data({
'b'
:
2
});
//两个参数 绑定数据对象
4.
console.log($(
'body'
).data(
'b'
));
//2
5.
console.log($(
'body'
).data();
//1 2
思路
回想下我们要解决什么问题:我们想在DOM上添加数据,但是不想引起内存的泄露,也就是我们不想引起循环引用,要尽量减少在DOM上挂数据。jquery的思路是这样:使用一个数据缓存对象$.cache,在需要绑定数据的DOM上扩展一个expando属性,这个属性存的是一个id,这里不会存在循环引用的情况了,之后将数据存在$.cache[id]上,当我们取DOM上的数据的时候,我们可以根据DOM上的expando找到id,进而找到存在$.cache[id]上的数据。可以看出jquery只是在DOM上扩展了一个属性expando,数据都存在了$.cache中,利用expando这个属性建立DOM和缓存对象之间的联系。无论我们添加多少的数据都会存储在缓存对象中,而不是直接挂在DOM上。这个唯一id是一个整型值,初始为0,调用data接口时自动加一,唯一id附加在以$.expando命名的属性上,$.expando是动态生成的,类似于一个时间戳,以尽可能的避免与用户变量冲突。从匹配的DOM元素上取到唯一id,在$.cache中找到唯一id对应的对象,再从对应的对象中找到key对应的值
看例子,在源码里打断点看一下
1.
$.data($(
'body'
)[
0
],{
'a'
:
1
});
2.
console.log($.data($(
'body'
)[
0
],
'a'
));
DOM对象扩展了一个属性,这个属性存的是cache的id。
这样大家就比较明显了。
实现
expando就是一个类似时间戳的东东,源码
1.
expando:
'jQuery'
+ ( jQuery.fn.jquery + Math.random() ).replace( /D/g,
''
)
就是为了生成标识的,没啥可说的。
这是静态方法的代码的整体结构,我看到的1.10.2,变化较大,所有的方法的实现都封装成了函数,主要看 internalData( elem, name, data )这个函数,其他的大伙自己看看吧
01.
jQuery.extend({
02.
cache: {},
03.
04.
// The following elements throw uncatchable exceptions if you
05.
// attempt to add expando properties to them.
06.
noData: {
07.
'applet'
:
true
,
08.
'embed'
:
true
,
09.
// Ban all objects except for <a href="http://www.it165.net/design/wfl/" target="_blank" class="keylink">Flash</a> (which handle expandos)
10.
'object'
:
'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'
11.
},
12.
13.
hasData: function( elem ) {
14.
elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
15.
return
!!elem && !isEmptyDataObject( elem );
16.
},
17.
18.
data: function( elem, name, data ) {
19.
return
internalData( elem, name, data );
20.
},
21.
22.
removeData: function( elem, name ) {
23.
return
internalRemoveData( elem, name );
24.
},
25.
26.
// For internal use only.
27.
_data: function( elem, name, data ) {
28.
return
internalData( elem, name, data,
true
);
29.
},
30.
31.
_removeData: function( elem, name ) {
32.
return
internalRemoveData( elem, name,
true
);
33.
},
34.
35.
// A method for determining if a DOM node can handle the data expando
36.
acceptData: function( elem ) {
37.
// Do not set data on non-element because it will not be cleared (#8335).
38.
if
( elem.nodeType && elem.nodeType !==
1
&& elem.nodeType !==
9
) {
39.
return
false
;
40.
}
41.
42.
var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
43.
44.
// nodes accept data unless otherwise specified; rejection can be conditional
45.
return
!noData || noData !==
true
&& elem.getAttribute(
'classid'
) === noData;
46.
}
47.
});
01.
function internalData( elem, name, data, pvt
/* Internal Use Only */
){
02.
if
( !jQuery.acceptData( elem ) ) {
//查看是否可以接受数据
03.
return
;
04.
}
05.
var ret, thisCache,
06.
internalKey = jQuery.expando,
//jQuery副本的唯一标识
07.
// We have to handle DOM nodes and JS objects differently because IE6-7
08.
// can't GC object references properly across the DOM-JS boundary
09.
isNode = elem.nodeType,
//判断DOM节点
10.
// Only DOM nodes need the global jQuery cache; JS object data is
11.
// attached directly to the object so GC can occur automatically
12.
cache = isNode ? jQuery.cache : elem,
//若是是DOM对象,则cache就是$.cache,否则为参数elem对象
13.
// Only defining an ID for JS objects if its cache already exists allows
14.
// the code to shortcut on the same path as a DOM node with no cache
15.
id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
//找id,id可能在DOM[expando]中,也可以在elem[expando]中
16.
// Avoid doing any more work than we need to when trying to get data on an
17.
// object that has no data at all
18.
if
( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name ===
'string'
) {
19.
return
;
//参数的一些判断限制
20.
}
21.
if
( !id ) {
//id不存在
22.
// Only DOM nodes need a new unique ID for each element since their data
23.
// ends up in the global cache
24.
if
( isNode ) {
//是DOM节点
25.
id = elem[ internalKey ] = core_deletedIds.pop() || jQuery.guid++;
//生成一个id
26.
}
else
{
//不是DOM,是一个对象
27.
id = internalKey;
//那么id就是那个expando
28.
}
29.
}
30.
if
( !cache[ id ] ) {
//cache中不存在数据,先弄成空的,一会在填充
31.
// Avoid exposing jQuery metadata on plain JS objects when the object
32.
// is serialized using JSON.stringify
33.
cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
34.
}
35.
// An object can be passed to jQuery.data instead of a key/value pair; this gets
36.
// shallow copied over onto the existing cache
37.
if
( typeof name ===
'object'
|| typeof name ===
'function'
) {
//处理第二个参数时对象或者是函数的情况
38.
if
( pvt ) {
//不太懂
39.
cache[ id ] = jQuery.extend( cache[ id ], name );
40.
}
else
{
//添加到data属性上
41.
cache[ id ].data = jQuery.extend( cache[ id ].data, name );
42.
}
43.
}
44.
thisCache = cache[ id ];
45.
// jQuery data() is stored in a separate object inside the object's internal data
46.
// cache in order to avoid key collisions between internal data and user-defined
47.
// data.
48.
if
( !pvt ) {
49.
if
( !thisCache.data ) {
50.
thisCache.data = {};
51.
}
52.
thisCache = thisCache.data;
53.
}
54.
if
( data !== undefined ) {
//第三个参数存在,就是存数据
55.
thisCache[ jQuery.camelCase( name ) ] = data;
56.
}
57.
// Check for both converted-to-camel and non-converted data property names
58.
// If a data property was specified
59.
if
( typeof name ===
'string'
) {
60.
61.
// First Try to find as-is property data
62.
ret = thisCache[ name ];
//取出来待返回的那个value
63.
//有啥用 这么麻烦
64.
// Test for null|undefined property data
65.
if
( ret ==
null
) {
66.
// Try to find the camelCased property
67.
ret = thisCache[ jQuery.camelCase( name ) ];
68.
}
69.
}
else
{
70.
ret = thisCache;
//就是返回存进来的那个对象或者函数
71.
}
72.
return
ret;
73.
}
实现起来还是比较简单的,只是有些地方jquery考虑的太周全了,我等凡人看不太透彻。
pS:给DOM对象添加的数据是存储在了$.cache中,而给对象添加书数据直接挂在了对象的expando上面。其实给一个对象挂数据也没有什么实际的意义。
看源码可以知道,看个例子更明显
1.
var obj = {};
2.
$.data(obj,{
'a'
:
1
});
3.
console.log($.data(obj,
'a'
));
4.
console.log(obj);
结果:
实例方法data()其实就是调用了$.data()这个静态方法,这里就不说了。
01.
jQuery.fn.extend({
02.
data: function( key, value ) {
03.
var attrs, name,
04.
data =
null
,
05.
i =
0
,
06.
elem =
this
[
0
];
07.
08.
// Special expections of .data basically thwart jQuery.access,
09.
// so implement the relevant behavior ourselves
10.
11.
// Gets all values
12.
if
( key === undefined ) {
13.
if
(
this
.length ) {
14.
data = jQuery.data( elem );
15.
16.
if
( elem.nodeType ===
1
&& !jQuery._data( elem,
'parsedAttrs'
) ) {
17.
attrs = elem.attributes;
18.
for
( ; i < attrs.length; i++ ) {
19.
name = attrs[i].name;
20.
21.
if
( name.indexOf(
'data-'
) ===
0
) {
22.
name = jQuery.camelCase( name.slice(
5
) );
23.
24.
dataAttr( elem, name, data[ name ] );
25.
}
26.
}
27.
jQuery._data( elem,
'parsedAttrs'
,
true
);
28.
}
29.
}
30.
31.
return
data;
32.
}
33.
34.
// Sets multiple values
35.
if
( typeof key ===
'object'
) {
36.
return
this
.each(function() {
37.
jQuery.data(
this
, key );
38.
});
39.
}
40.
41.
return
arguments.length >
1
?
42.
43.
// Sets one value
44.
this
.each(function() {
45.
jQuery.data(
this
, key, value );
//这是重点
46.
}) :
47.
48.
// Gets one value
49.
// Try to fetch any internally stored data first
50.
elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) :
null
;
51.
},
问题
现在我们利用源码分析一些问题
01.
var a = $(
'body'
);
02.
var b = $(
'body'
);
03.
a.data(
'a'
,
1
);
04.
b.data(
'a'
,
2
);
05.
console.log(a.data(
'a'
));
//2
06.
console.log(b.data(
'a'
));
//2
07.
08.
$.data(a,
'b'
,
1
);
09.
$.data(b,
'b'
,
2
);
10.
console.log($.data(a,
'b'
))
//1
11.
console.log($.data(b,
'b'
))
//2
12.
13.
$.data(a[
0
],
'b'
,
1
);
14.
$.data(b[
0
],
'b'
,
2
);
15.
console.log($.data(a[
0
],
'b'
));
//2
16.
console.log($.data(b[
0
],
'b'
));
//2
看着有些晕,先看下这个
1.
var a = $(
'body'
);
2.
var b = $(
'body'
);
3.
console.log(a[
0
] == b[
0
]);
//true
4.
console.log(a == b);
//false
5.
console.log( $(
'body'
) == $(
'body'
));
//false
每一次$('body')都生成一个新的对象,所以每一次都会不同,$('body')[0]都是指向同一个body对象,a 和b指向的每个新对象的地址,所以不同。
看第一组
1.
var a = $(
'body'
);
2.
var b = $(
'body'
);
3.
a.data(
'a'
,
1
);
4.
b.data(
'a'
,
2
);
5.
console.log(a.data(
'a'
));
//2
6.
console.log(b.data(
'a'
));
//2
在看源代码这句
1.
this
.each(function() {
2.
jQuery.data(
this
, key, value );
3.
})
调用$.data(),但是这里第一个参数为this,是原生的DOM对象,第一组中的a和b的DOM对象都是body,所以添加数据会产生覆盖现象。
第二组和第二组是正常情况,不解释了。
小结
这就是我的理解,希望大家指正。以后会多分析jquery的实现过程,源码的细节太难了。
//
转:jQuery.data的更多相关文章
- jQuery data
大家会如何设计一个缓存呢? 一个简单的Cache (function(){ var __cache = {}, Cache = { get: function(__name){ return __ca ...
- jQuery.Data源码
jQuery.data的是jQuery的数据缓存系统.它的主要作用就是为普通对象或者DOM元素添加数据. 1 内部存储原理 这个原理很简单,原本要添加在DOM元素本身的数据,现在被集中的存储在cach ...
- jQuery源码解读 - 数据缓存系统:jQuery.data
jQuery在1.2后引入jQuery.data(数据缓存系统),主要的作用是让一组自定义的数据可以DOM元素相关联——浅显的说:就是让一个对象和一组数据一对一的关联. 一组和Element相关的数据 ...
- JQuery data API实现代码分析
JQuery data 接口是什么? .data() Store arbitrary data associated with the matched elements or return the v ...
- HTML5 自定义属性 data-* 和 jQuery.data 详解
新的HTML5标准允许你在普通的元素标签里,嵌入类似data-*的属性,来实现一些简单数据的存取.它的数量不受限制,并且也能由javascript动态修改,也支持CSS选择器进行样式设置.这使得dat ...
- jquery data方法
jquery.data()文档:http://api.jquery.com/jQuery.data/ html5有个data-*属性,跟这个功能一样. Note: This is a low-leve ...
- jquery data方法取值与js attr取值的区别
<a data-v="3"></a> jquery data方法的运行机制: 第一次查找dom,使用attributes获取到dom节点值,并将其值存到缓存 ...
- jQuery.data的是jQuery的数据缓存系统
jQuery.Data源码 jQuery.data的是jQuery的数据缓存系统 jQuery.data的是jQuery的数据缓存系统.它的主要作用就是为普通对象或者DOM元素添加数据. 1 内部存储 ...
- 读jQuery源码 jQuery.data
var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, rmultiDash = /([A-Z])/g; function internalData( elem, n ...
随机推荐
- 【Java基础】方法
Num1:检查参数的有效性 绝大多数的方法和构造器对于传递给它们的参数值都会有某些限制.比如:索引值必须是非负数,对象引用不能为null等等.这些都很常见,你应该在文档中清楚地指明所有这些限制,并在方 ...
- JAVA 设计模式 策略模式
用途 Title 它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户. 策略模式是一种行为型模式. 结构
- MFC抓网页
CString chinachar_str("读取的东西:"); CInternetSession sion(NULL,); CHttpFile *http=NULL; CStri ...
- [译]学习IPython进行交互式计算和数据可视化(三)
第二章 在本章中,我们将详细学习IPython相对以Python控制台带来的多种改进.特别的,我们将会进行下面的几个任务: 从IPython中使用系统shell以在shell和Python之间进行强大 ...
- 8.Fluent API in Code-First【Code-First系列】
在前面的章节中,我们已经看到了各种不同的数据注解特性.现在我们来学习一下Fluent API. Fluent API是另外一种配置领域类的方式,它提供了更多的配置相比数据注解特性. Mappings[ ...
- C#控件及常用设计整
C#控件及常用设计整 1.窗体 1 2.Label 控件 3 3.TextBox 控件 4 4.RichTextBox控件 5 5.NumericUpDown 控件 7 ...
- Android 获取可靠的手机编码
项目中出现了将设备和用户信息进行绑定的需求.最先想到的是IMEI串码和IMSI串码.手机登陆的时候一直都没有问题.换了一个平板中之后IMEI和IMSI串码都获取不到了.后来查了一下原因,是因为平板上是 ...
- 交换排序---快速排序算法(Javascript版)
快速排序是对冒泡排序的一种改进.通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行 ...
- ASP.NET中Request.RawUrl、Request.Url的区别
如果访问的地址是: http://hovertree.com/guestbook/addmessage.aspx?key=hovertree%3C&n=myslider#zonemenu 那么 ...
- Redis系列四之复制
一.复制基本配置与演示 为了避免单点故障,Redis提供了复制功能,可以实现自动同步的过程. 1.配置 同步后的数据分为两类:一类是主数据库(master),一类是从数据库(slave).主数据库可以 ...