以下文章的前半部分是我看的時(shí)候從網(wǎng)絡(luò)上面找的一篇指導(dǎo)性質(zhì)的文章,寫的還不錯(cuò),相同的道理和話就不重復(fù)的說了,主要是語言表達(dá)能力也不好。所以我拿過來用用,后半段是我自己的解讀。
我們提供的服務(wù)有:網(wǎng)站設(shè)計(jì)制作、網(wǎng)站設(shè)計(jì)、微信公眾號(hào)開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、南江ssl等。為千余家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的南江網(wǎng)站制作公司
考慮到作者的版權(quán)問題我們附上作者原文章地址:http://www.cnblogs.com/aaronjs/p/3370176.html
一般jQuery開發(fā),我們都喜歡便捷式的把很多屬性,比如狀態(tài)標(biāo)志都寫到dom節(jié)點(diǎn)中,也就是HTMLElement
好處:直觀,便捷
壞處:
循環(huán)引用
直接暴露數(shù)據(jù),安全性?
增加一堆的自定義屬性標(biāo)簽,對(duì)瀏覽器來說是沒意義的
取數(shù)據(jù)的時(shí)候要對(duì)HTML節(jié)點(diǎn)做操作
什么是內(nèi)存泄露
內(nèi)存泄露是指一塊被分配的內(nèi)存既不能使用,又不能回收,直到瀏覽器進(jìn)程結(jié)束。在C++中,因?yàn)槭鞘謩?dòng)管理內(nèi)存,內(nèi)存泄露是經(jīng)常出現(xiàn)的事情。而現(xiàn)在流行的C#和Java等語言采用了自動(dòng)垃圾回收方法管理內(nèi)存,正常使用的情況下幾乎不會(huì)發(fā)生內(nèi)存泄露。瀏覽器中也是采用自動(dòng)垃圾回收方法管理內(nèi)存,但由于瀏覽器垃圾回收方法有bug,會(huì)產(chǎn)生內(nèi)存泄露。
內(nèi)存泄露的幾種情況
循環(huán)引用
Javascript閉包
DOM插入順序
一個(gè)DOM對(duì)象被一個(gè)Javascript對(duì)象引用,與此同時(shí)又引用同一個(gè)或其它的Javascript對(duì)象,這個(gè)DOM對(duì)象可能會(huì)引發(fā)內(nèi)存泄漏。這個(gè)DOM對(duì)象的引用將不會(huì)在腳本停止的時(shí)候被垃圾回收器回收。要想破壞循環(huán)引用,引用DOM元素的對(duì)象或DOM對(duì)象的引用需要被賦值為null。
含有DOM對(duì)象的循環(huán)引用將導(dǎo)致大部分當(dāng)前主流瀏覽器內(nèi)存泄露
第一種:多個(gè)對(duì)象循環(huán)引用

var a=new Object;var b=new Object; a.r=b; b.r=a;

第二種:循環(huán)引用自己
var a=new Object; a.r=a;
循環(huán)引用很常見且大部分情況下是無害的,但當(dāng)參與循環(huán)引用的對(duì)象中有DOM對(duì)象或者ActiveX對(duì)象時(shí),循環(huán)引用將導(dǎo)致內(nèi)存泄露。
我們把例子中的任何一個(gè)new Object替換成document.getElementById或者document.createElement就會(huì)發(fā)生內(nèi)存泄露了。
具體的就深入討論了,這里的總結(jié)
JS的內(nèi)存泄露,無怪乎就是從DOM中remove了元素,但是依然有變量或者對(duì)象引用了該DOM對(duì)象。然后內(nèi)存中無法刪除。使得瀏覽器的內(nèi)存占用居高不下。這種內(nèi)存占用,隨著瀏覽器的刷新,會(huì)自動(dòng)釋放。
而另外一種情況,就是循環(huán)引用,一個(gè)DOM對(duì)象和JS對(duì)象之間互相引用,這樣造成的情況更嚴(yán)重一些,即使刷新,內(nèi)存也不會(huì)減少。這就是嚴(yán)格意義上說的內(nèi)存泄露了。
所以在平時(shí)實(shí)際應(yīng)用中, 我們經(jīng)常需要給元素緩存一些數(shù)據(jù),并且這些數(shù)據(jù)往往和DOM元素緊密相關(guān)。由于DOM元素(節(jié)點(diǎn))也是對(duì)象, 所以我們可以直接擴(kuò)展DOM元素的屬性,但是如果給DOM元素添加自定義的屬性和過多的數(shù)據(jù)可能會(huì)引起內(nèi)存泄漏,所以應(yīng)該要盡量避免這樣做。 因此更好的解決方法是使用一種低耦合的方式讓DOM和緩存數(shù)據(jù)能夠聯(lián)系起來。
jQuery引入緩存的作用
允許我們?cè)贒OM元素上附加任意類型的數(shù)據(jù),避免了循環(huán)引用的內(nèi)存泄漏風(fēng)險(xiǎn)
用于存儲(chǔ)跟dom節(jié)點(diǎn)相關(guān)的數(shù)據(jù),包括事件,動(dòng)畫等
一種低耦合的方式讓DOM和緩存數(shù)據(jù)能夠聯(lián)系起來
jQuery緩存系統(tǒng)的真正魅力在于其內(nèi)部應(yīng)用中,動(dòng)畫、事件等都有用到這個(gè)緩存系統(tǒng)。試想如果動(dòng)畫的隊(duì)列都存儲(chǔ)到各DOM元素的自定義屬性中,這樣雖然可以方便的訪問隊(duì)列數(shù)據(jù),但也同時(shí)帶來了隱患。如果給DOM元素添加自定義的屬性和過多的數(shù)據(jù)可能會(huì)引起內(nèi)存泄漏,所以要盡量避免這么干。
數(shù)據(jù)緩存接口
對(duì)于jQuery.data方法,原文如下
The jQuery.data() method allows us to attach data of any type to DOM elements in a way that is safe from circular references and therefore from memory leaks. We can set several distinct values for a single element and retrieve them later:
在jQuery的官方文檔中,提示用戶這是一個(gè)低級(jí)的方法,應(yīng)該用.data()方法來代替。$.data( element, key, value )可以對(duì)DOM元素附加任何類型的數(shù)據(jù),但應(yīng)避免循環(huán)引用而導(dǎo)致的內(nèi)存泄漏問題
都是用來在元素上存放數(shù)據(jù)也就平時(shí)所說的數(shù)據(jù)緩存,都返回jQuery對(duì)象,但是內(nèi)部的處理確有本質(zhì)的區(qū)別
我們看一組對(duì)比
<div id="aaron">Aron test</div>

var aa1=$("#aaron");var aa2=$("#aaron");//=======第一組=========$(''
).data()方法aa1.data('a',1111);
aa2.data('a',2222);
aa1.data('a') //結(jié)果222222aa2.data('a') //結(jié)果222222//=======第二組=========$.data()方法$.data(aa1,"b","1111")
$.data(aa2,"b","2222")
$.data(aa1,"b") //結(jié)果111111$.data(aa2,"b") //結(jié)果222222
意外嗎?,這樣的細(xì)節(jié)以前是否注意到呢?
怎么通過.data()方法會(huì)覆蓋前面key相同的值呢?
對(duì)于jQuery來說,數(shù)據(jù)緩存系統(tǒng)本來就是為事件系統(tǒng)服務(wù)而分化出來的,到后來,它的事件克隆乃至后來的動(dòng)畫列隊(duì)實(shí)現(xiàn)數(shù)據(jù)的存儲(chǔ)都是離不開緩存系統(tǒng),所以數(shù)據(jù)緩存也算是jQuery的一個(gè)核心基礎(chǔ)了
早期jQuery的緩存系統(tǒng)是把所有數(shù)據(jù)都放$.cache之上,然后為每個(gè)要使用緩存系統(tǒng)的元素節(jié)點(diǎn),文檔對(duì)象與window對(duì)象分配一個(gè)UUID
data的實(shí)現(xiàn)不像attr直接把數(shù)據(jù)作為屬性捆綁到元素節(jié)點(diǎn)上,如果為DOM Element 附加數(shù)據(jù);DOM Element 也是一種 Object ,但 IE6、IE7 對(duì)直接附加在 DOM Element 上的對(duì)象的垃圾回收存在問題;因此我們將這些數(shù)據(jù)存放在全局緩存(我們稱之為“globalCache”)中,即 “globalCache” 包含了多個(gè) DOM Element 的 “cache”,并在 DOM Element 上添加一個(gè)屬性,存放 “cache” 對(duì)應(yīng)的 uid
$().data('a') 在表現(xiàn)形式上,雖然是關(guān)聯(lián)到dom上的,但是實(shí)際上處理就是在內(nèi)存區(qū)開辟一個(gè)cache的緩存
那么JQuery內(nèi)部是如何處理,各種關(guān)聯(lián)情況與操作呢?
******************$(‘’).data()的實(shí)現(xiàn)方式********************
用name和value為對(duì)象附加數(shù)據(jù)
var obj = {};
$.data(obj, 'name', 'aaron');
$.data(obj,'name') //aaron
一個(gè)對(duì)象為對(duì)象附加數(shù)據(jù)

var obj = {};
$.data(obj, {
name1: 'aaron1',
name2: 'aaron1'
});
$.data(obj) //Object {name1: "aaron1", name2: "aaron1"}
為 DOM Element 附加數(shù)據(jù)
我們用最簡單的代碼來闡述這個(gè)處理的流程:
1.獲取節(jié)點(diǎn)body
var $body = $("body")2.給body上增加一條數(shù)據(jù),屬性為foo,值為52
$body.data("foo", 52);3.取出foo
$body.data('foo')
考慮一個(gè)問題:
一個(gè)元素在正常情況下可以使用.remove()方法將其刪除,并清除各自的數(shù)據(jù)。但對(duì)于本地對(duì)象而言,這是不能徹底刪除的,這些相關(guān)的數(shù)據(jù)一直持續(xù)到窗口對(duì)象關(guān)閉
同樣,這些問題也存在于event 對(duì)象中,因?yàn)槭录幚砥?handlers)也是用該方法來存儲(chǔ)的。
那么,要解決該問題最簡單的方法是將數(shù)據(jù)存儲(chǔ)到本地對(duì)象新增的一個(gè)屬性之中
所以如流程二解析一樣增加一個(gè)unlock標(biāo)記
cache與elem 都統(tǒng)一起來

if ( elem.nodeType ) {
cache[ id ] = dataObject;
elem[ expando ] = id;
} else {
elem[ expando ] = dataObject;
}
**************實(shí)現(xiàn)解析****************
(1)先在jQuery內(nèi)部創(chuàng)建一個(gè)cache對(duì)象{}, 來保存緩存數(shù)據(jù)。 然后往需要進(jìn)行緩存的DOM節(jié)點(diǎn)上擴(kuò)展一個(gè)值為expando的屬性,

function Data() {
Object.defineProperty( this.cache = {}, 0, {
get: function() { return {};
}
}); this.expando = jQuery.expando + Math.random();
}
注:expando的值,用于把當(dāng)前數(shù)據(jù)緩存的UUID值做一個(gè)節(jié)點(diǎn)的屬性給寫入到指定的元素上形成關(guān)聯(lián)橋梁,所以,所以元素本身具有這種屬性的可能性很少,所以可以忽略沖突。
(2)接著把每個(gè)節(jié)點(diǎn)的dom[expando]的值都設(shè)為一個(gè)自增的變量id,保持全局唯一性。 這個(gè)id的值就作為cache的key用來關(guān)聯(lián)DOM節(jié)點(diǎn)和數(shù)據(jù)。也就是說cache[id]就取到了這個(gè)節(jié)點(diǎn)上的所有緩存,即id就好比是打開一個(gè)房間(DOM節(jié)點(diǎn))的鑰匙。 而每個(gè)元素的所有緩存都被放到了一個(gè)map映射里面,這樣可以同時(shí)緩存多個(gè)數(shù)據(jù)。
Data.uid = 1;
關(guān)聯(lián)起dom對(duì)象與數(shù)據(jù)緩存對(duì)象的一個(gè)索引標(biāo)記,換句話說
先在dom元素上找到expando對(duì)應(yīng)值,也就uid,然后通過這個(gè)uid找到數(shù)據(jù)cache對(duì)象中的內(nèi)容
(3)所以cache對(duì)象結(jié)構(gòu)應(yīng)該像下面這樣:

var cache = { "uid1": { // DOM節(jié)點(diǎn)1緩存數(shù)據(jù),
"name1": value1, "name2": value2
}, "uid2": { // DOM節(jié)點(diǎn)2緩存數(shù)據(jù),
"name1": value1, "name2": value2
} // ......};
每個(gè)uid對(duì)應(yīng)一個(gè)elem緩存數(shù)據(jù),每個(gè)緩存對(duì)象是可以由多個(gè)name/value(名值對(duì))對(duì)組成的,而value是可以是任何數(shù)據(jù)類型的。
流程分解:(復(fù)雜的過濾,找重的過程去掉)
第一步:jQuery本身就是包裝后的數(shù)組結(jié)構(gòu),這個(gè)不需要解析了
第二步:通過data存儲(chǔ)數(shù)據(jù)
為了把不把數(shù)據(jù)與dom直接關(guān)聯(lián),所以會(huì)把數(shù)據(jù)存儲(chǔ)到一個(gè)cache對(duì)象上
產(chǎn)生一個(gè) unlock = Data.uid++; unlock 標(biāo)記號(hào)
把unlock標(biāo)記號(hào),作為一個(gè)屬性值 賦予$body節(jié)點(diǎn)
cache緩存對(duì)象中開辟一個(gè)新的空間用于存儲(chǔ)foo數(shù)據(jù),this.cache[ unlock ] = {};
最后把foo數(shù)據(jù)掛到cache上,cache[ data ] = value;
第三步:通過data獲取數(shù)據(jù)
從$body節(jié)點(diǎn)中獲取到unlock標(biāo)記
通過unlock在cache中取到對(duì)應(yīng)的數(shù)據(jù)
流程圖:

整個(gè)過程結(jié)束,其實(shí)分解后邏輯很簡單的,只是要處理各種情況下,代碼結(jié)構(gòu)封裝就顯得很復(fù)雜了
Body元素:expando:uid
jQuery203054840829130262140.37963378243148327: 3
數(shù)據(jù)緩存cache
uid:Object
那么jQuery.data() 與 .data() 有什么區(qū)別?
1.jQuery.data(element,[key],[value])源代碼

jQuery.extend({
acceptData: Data.accepts,
hasData: function( elem ){}, //直接調(diào)用 data_user.access 數(shù)據(jù)類的接口,傳入的是elem整個(gè)jQuery對(duì)象
data: function( elem, name, data ) { return data_user.access( elem, name, data );
},
........
2.data([key],[value])

jQuery.fn.extend({
data: function( elem, name, data ) { return jQuery.access( this, function( value )){ //區(qū)別在each方法了,處理的是每一個(gè)元素dom節(jié)點(diǎn)
this.each(function() {
}
}
}
},
........
源代碼從源碼的簡單對(duì)比就很明顯的看出來
看jQuery.data(element,[key],[value]),每一個(gè)element都會(huì)有自己的一個(gè){key:value}對(duì)象保存著數(shù)據(jù),所以新建的對(duì)象就算有key相同它也不會(huì)覆蓋原來存在的對(duì)象key所對(duì)應(yīng)的value,因?yàn)樾聦?duì)象保存是是在另一個(gè){key:value}對(duì)象中
$("div").data("a","aaaa") 它是把數(shù)據(jù)綁定每一個(gè)匹配div節(jié)點(diǎn)的元素上
源碼可以看出來,說到底,數(shù)據(jù)緩存就是在目標(biāo)對(duì)象與緩存體間建立一對(duì)一的關(guān)系,整個(gè)Data類其實(shí)都是圍繞著 thia.cache 內(nèi)部的數(shù)據(jù)做 增刪改查的操作
為了更準(zhǔn)確的理解jq的data函數(shù)處理,我自己寫了一個(gè)方法來實(shí)現(xiàn)data,數(shù)據(jù)緩存:
var $ = {};
//這個(gè)模擬的就是jquery中的全局的緩存
$.catche = {};
//這個(gè)就是模擬了jq的全局緩存的一個(gè)標(biāo)志key
$.internalKey = (new Date()).getTime();
//這個(gè)就是類uuid的用法
$.prop_index = 1;
//獲取得到元素
var obj = document.querySelector("#testDiv");
/***
*這就是模擬jq的data方法做的數(shù)據(jù)緩存
**/
function data(obj,attr,val){
//判斷obj是否是節(jié)點(diǎn)
var isNode = obj.nodeType;
var ele;
//參數(shù)修正
if(!isNode){
if(arguments.length <= 2){
val = attr;
attr = obj;
ele = this;
}
}else{
ele = obj;
}
isNode = ele.nodeType;
//獲得數(shù)據(jù)的id
var id = isNode?ele[$.internalKey]:ele[$.internalKey]&&$.internalKey;
//如果不存在id
if(!id){
//在jq里面還實(shí)現(xiàn)了,從已經(jīng)刪除的id序列里面啟用刪除過的id
id = isNode?ele[$.internalKey]= $.prop_index++: $.internalKey;
}
var valObj = isNode?$.catche:ele;
if(!valObj[id]){
valObj[id] = {};
}
var returnData = "";
if(typeof attr == "string"){
if(val != undefined)
{
returnData = val;
valObj[id][attr] = val;
}else{
returnData = valObj[id][attr];
}
}else{
returnData = valObj[id];
}
$.catche = valObj;
return returnData;
}
obj.data = data;
// console.log(data(obj,"hell","1212"));
// console.log(data(obj,"hell"));
obj.data("hello","aaa");
console.log("val:"+obj.data("hello"));通過自己的測試實(shí)現(xiàn)了jq的data的數(shù)據(jù)緩存,也無需在dom元素節(jié)點(diǎn)上面添加任何屬性,這個(gè)大概就是jquery的實(shí)現(xiàn)data數(shù)據(jù)緩存的機(jī)制。如果有理解不當(dāng)?shù)牡胤酵刚?/p>
網(wǎng)站名稱:jqeury數(shù)據(jù)緩存之data()解析
文章URL:http://chinadenli.net/article34/gdghpe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供移動(dòng)網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)、標(biāo)簽優(yōu)化、靜態(tài)網(wǎng)站、網(wǎng)站維護(hù)、電子商務(wù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)