日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

jQuery-1.9.1源码分析系列(四) 缓存系统

發(fā)布時間:2023/11/27 生活经验 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 jQuery-1.9.1源码分析系列(四) 缓存系统 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

  先前在分析Sizzle的時候分析到Sizzle有自己的緩存機制,點擊這里查看。不過Sizzle的緩存只是對內(nèi)使用的(內(nèi)部自己存,自己取)。接下來分析jQuery可以對外使用的緩存(可存可取)。

  首先需要明白jQuery緩存需要解決什么問題,實現(xiàn)它的意義?

  jQuery緩存要解決的是在往DOM節(jié)點添加數(shù)據(jù)(這些數(shù)據(jù)往往和該DOM節(jié)點緊密相關(guān)),但是給DOM添加數(shù)據(jù)或自定義屬性可能起內(nèi)存泄漏(DOM發(fā)生緩存泄漏導(dǎo)致DOM的數(shù)據(jù)沒法被刪除,那么添加到DOM的數(shù)據(jù)也無法被回收。久而久之,會出現(xiàn)一大片內(nèi)存得不到釋放。),所以應(yīng)該要盡量避免這樣做。更好的解決方法是使用一種低耦合的方式讓DOM和緩存數(shù)據(jù)能夠聯(lián)系起來。

  jQuery怎么做?

  jQuery定義了一個屬性cache = {}來保存所有的緩存數(shù)據(jù)。在DOM節(jié)點上添加一個expando的值(expando的值等于”jQuery”+當前時間)為屬性名稱的屬性,這個屬性的值id = dom[jQuery.expando]用來查找jQuery.cache上對應(yīng)的緩存數(shù)據(jù):jQuery.cache[id].data 即為DOM對應(yīng)的緩存數(shù)據(jù)。為了保證了id?的全局唯一性,這個id使用jQuery.guid自增。例如

$("#demo").data("name","chua");
$("#demo").data("name");//"chua"

  jQuery底層接口還做了拓展,不僅僅能緩存在DOM節(jié)點的數(shù)據(jù),還可以緩存非DOM節(jié)點的對象的數(shù)據(jù)。這種方式最終數(shù)據(jù)是附加到了對象obj自己身上,而沒有使用全局緩存jQuery.cache。該方式也會在對象obj上添加一個expando的值為屬性名稱的屬性,緩存數(shù)據(jù)保存在obj[jQuery.expando].data上。不過這個一般不推薦外部使用,因為調(diào)用的是更底層的api: jQuery.data,而非$(...).data。例子:

var obj = {};
$.data(obj,'name','chua');
$.data(obj,'name');//"chua"

  

  接下來我們開始解析源碼。

jQuery.fn.extend({data: function( key, value ) {…    },//內(nèi)部使用基礎(chǔ)api:jQuery.data來實現(xiàn),可以使用參數(shù)(key,value),也可以使用參數(shù)(obj)removeData: function( key ) {…}//內(nèi)部使用基礎(chǔ)api: jQuery.removeData來實現(xiàn)
});

所以我們主要看底層基礎(chǔ)API部分

jQuery.extend({cache: {},// 每個jQuery拷貝都有一個其唯一的標志。比如你的頁面有兩個iframe且每個iframe都用到的jQuery。name你的兩個iframe就有兩份jQuery拷貝。expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),// 下面的元素將拋出不可捕獲的異常,如果你嘗試給他們添加expando屬性//主要用在acceptData函數(shù)中確定元素是否可以添加expando屬性
    noData: {"embed": true,// Ban all objects except for Flash (which handle expandos)"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000","applet": true},hasData: function( elem ) {…},data: function( elem, name, data ) {return internalData( elem, name, data );},removeData: function( elem, name ) {return internalRemoveData( elem, name );},// 內(nèi)部使用_data: function( elem, name, data ) {return internalData( elem, name, data, true );},_removeData: function( elem, name ) {return internalRemoveData( elem, name, true );},// 用來確定DOM節(jié)點是否能夠添加expando 數(shù)據(jù)acceptData: function( elem ) {// non-element不能添加數(shù)據(jù)if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) {return false;}var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];// nodes accept data unless otherwise specified; rejection can be conditionalreturn !noData || noData !== true && elem.getAttribute("classid") === noData;}
});

  所以歸根結(jié)底緩存數(shù)據(jù)和取出緩存數(shù)據(jù)是使用內(nèi)部函數(shù)internalData( elem, name, data, pvt /* Internal Use Only */ )

  

a. 存取緩存的內(nèi)部函數(shù)function internalData( elem, name, data, pvt /* Internal Use Only */ )


?  internalData的處理流程為:

  1.判斷如果元素不支持添加屬性的直接返回。取出緩存容器和緩存中使用的ID備用。這里面粉兩種情況,如果傳遞的對象elem是DOM對象,則緩存取全局緩存容器jQuery.cache,id取elem[jQuery.expando](如果沒有的話使用jQuery.guid自增值設(shè)置一個);如果elem不是DOM對象,則緩存容器取對象本身elem,id取jQuery.expando

var thisCache, ret,//獲取每份jquery拷貝的標志internalKey = jQuery.expando,getByName = typeof name === "string",//我們將DOM節(jié)點和JS對象區(qū)分開來,因為IE6-7不能跨越DOM-JS界限正確回收對象引用//所有DOM節(jié)點上沒有附加數(shù)據(jù),而是存放再來全局jQuery緩存jQuery.cache中isNode = elem.nodeType,//只有DOM節(jié)點需要全局jQuery緩存;js對象數(shù)據(jù)直接附加到對象上使得垃圾回收機制能夠自動回收cache = isNode ? jQuery.cache : elem,//假如JS對象的緩存已經(jīng)存在,則拷貝標志作為一個訪問ID。//如果是DOM對象,則拷貝標志作為一個屬性附加到dom上,這個屬性的值作為訪問全局緩存的一個路徑IDid = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;//預(yù)先處理要獲取數(shù)據(jù),但是緩存中沒有數(shù)據(jù)的情況。這種情況應(yīng)當直接返回if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {return;}if ( !id ) {//只有dom節(jié)點的每個元素都需要唯一的ID,直到他們的數(shù)據(jù)在全局緩存中清除if ( isNode ) {//啟用刪除的id中最后一個id,或是對象的全局GUID統(tǒng)計elem[ internalKey ] = id = core_deletedIds.pop() || jQuery.guid++;} else {id = internalKey;}}
View Code

  2.取出的緩存容器cache[ id ]如果不存在就初始化為空對象

        //如果緩存中沒有數(shù)據(jù)if ( !cache[ id ] ) {//初始化對象cache[ id ] = {};//避免在對象使用JSON.stringify序列化的時候暴露jQuery的元數(shù)據(jù)給普通對象if ( !isNode ) {cache[ id ].toJSON = jQuery.noop;}}
View Code

  3.cache[ id ]并非真正的緩存數(shù)據(jù),真正的緩存數(shù)據(jù)保存在cache[ id ].data上。在初始化之前需要對特殊情況(要緩存的數(shù)據(jù)是對象,且沒指定緩存名稱的時候,意味著要替換掉原來的整個緩存。比如:$.data(elem,{"name": "chua"})。)處理。如果cache[ id ].data不存在則初始化他。

    //使用對象的話用來替代key/value這種成對的方式。
//比如$.data(document,{name:'chenhua'})//添加后,直接通過$.data(document,'name')就可以獲取if ( typeof name === "object" || typeof name === "function" ) {//內(nèi)部使用的時候if ( pvt ) {//內(nèi)部使用直接添加到cache[ id ]上cache[ id ] = jQuery.extend( cache[ id ], name );} else {//添加緩存數(shù)據(jù)到cache[ id ].data上cache[ id ].data = jQuery.extend( cache[ id ].data, name );}}thisCache = cache[ id ];//非內(nèi)部使用if ( !pvt ) {//如果.data沒有初始化則先初始化if ( !thisCache.data ) {thisCache.data = {};}thisCache = thisCache.data;}
View Code

  4.如果是存數(shù)據(jù),則存之,返回整個緩存;取數(shù)據(jù)則取之,返回取得的數(shù)據(jù)

    //添加緩存數(shù)據(jù)if ( data !== undefined ) {thisCache[ jQuery.camelCase( name ) ] = data;}//如果name是字符串,則返回對應(yīng)的數(shù)據(jù),否則返回整個緩存if ( getByName ) {//通過本身neme或不同瀏覽器的駝峰寫法獲取緩存ret = thisCache[ name ];if ( ret == null ) {ret = thisCache[ jQuery.camelCase( name ) ];}} else {ret = thisCache;}return ret;
View Code

  ok。流程就到這里。

?

?  解析完jQuery.data的流程以后我們來做一組實驗,區(qū)分高級api:$(...).data和底層api:jQuery.data的區(qū)別。在jQuery的官方文檔中,提示用戶jQuery.data是一個低級的方法,應(yīng)該用$(...).data()方法來代替。$.data( element, key, value )可以對DOM元素附加任何類型的數(shù)據(jù),但應(yīng)避免循環(huán)引用而導(dǎo)致的內(nèi)存。

var t1=$(document);
var t2=$(document);//=======第一組$(…).data()方法
t1.data('age',0);
t2.data('age',1);
t1.data('age')  //1
t2.data('age')  //1//=======第二組$.data()方法
$.data(t1,"name","chua")
$.data(t2,"name","yling")
$.data(t1,"name")   //chua
$.data(t2,"name")   //yling

  可以看出其中的不同吧。使用$(...).data最終傳遞給internalData的第一個參數(shù)elem是節(jié)點對象document,所以使用全局緩存jQuery.cache保存。而使用$.data方式最終傳遞給internalData的第一個參數(shù)elem是jQuery對象[document],這樣的屬性結(jié)果是保存在對象自己身上,而t1和t2是不同的對象,分別取自己對象上的緩存,結(jié)果當然不同了。

  var t1=$(document);t1.data('age',0);的緩存效果

  

  $.data(t1,"name","chua")的緩存效果

  

  

b. 刪除緩存


  使用$(...).data(key,value)方式保存的緩存直接使用$(...).removeData(key)來刪除緩存。當然也可以使用低級方法$.data(elem,key,value)來緩存數(shù)據(jù),使用$.removeData(elem,key)來刪除緩存,但是建議不要使用低級方法。刪除緩存最終會調(diào)用內(nèi)部函數(shù)internalRemoveData( elem, name, pvt/*內(nèi)部使用,默認為false*/ )來處理。我們跟蹤一下處理流程

  1.判斷如果元素不支持添加屬性的直接返回。取出緩存容器和相應(yīng)的id。如果緩存容器不存在則直接返回

        //元素不支持添加屬性的直接返回if ( !jQuery.acceptData( elem ) ) {return;}var i, l, thisCache,isNode = elem.nodeType,//詳細信息查看jQuery.data源碼cache = isNode ? jQuery.cache : elem,id = isNode ? elem[ jQuery.expando ] : jQuery.expando;//緩存中沒有數(shù)據(jù)直接返回if ( !cache[ id ] ) {return;}
View Code

  2.如果有需要刪除的緩存名稱(參數(shù)name,可以是字符串【如果要刪除多個緩存名指定的緩存可以使用空格隔開】,也可以是數(shù)組),則根據(jù)名稱刪除元素。刪除后如果緩存不是空對象則返回。

        if ( name ) {//pvt內(nèi)部使用,默認為false(undefined)thisCache = pvt ? cache[ id ] : cache[ id ].data;//緩存對象if ( thisCache ) {//name為字串if ( !jQuery.isArray( name ) ) {// name就是一個keyif ( name in thisCache ) {name = [ name ];} else {//處理ie兼容,如果name是空格相連的字符串,使用split分割name = jQuery.camelCase( name );if ( name in thisCache ) {name = [ name ];} else {name = name.split(" ");}}//name為數(shù)組} else {//如果name是key的數(shù)組// 當數(shù)據(jù)初始化完成,通過("key", "val")簽名,// keys將轉(zhuǎn)化成駱駝寫法.// 如果沒有辦法告知_how_這種key被添加,移除原始類型的key和駱駝寫法的keyname = name.concat( jQuery.map( name, jQuery.camelCase ) );}for ( i = 0, l = name.length; i < l; i++ ) {delete thisCache[ name[i] ];}//如果緩存不為空則返回,如果為空則我們在后面通過delete刪除該對象if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {return;}}}
View Code

  3.如果沒有傳遞要刪除的緩存名,表示要刪除全部緩存。刪除緩存后如果緩存容器不為空對象則返回

        //詳細信息查看jQuery.data源碼if ( !pvt ) {delete cache[ id ].data;//cache不為空則返回if ( !isEmptyDataObject( cache[ id ] ) ) {return;}}
View Code

  4.走到最后一步,表示緩存容器內(nèi)的緩存都被清空了,刪除緩存容器

        //走到這一步表示cache中沒有數(shù)據(jù)了,銷毀cacheif ( isNode ) {jQuery.cleanData( [ elem ], true );// 當支持刪除expandos 或'cache'不是window (#10080)} else if ( jQuery.support.deleteExpando || cache != cache.window ) {delete cache[ id ];// 當所有都失敗時,處理為null} else {cache[ id ] = null;}
View Code

  附上完整源碼

function internalRemoveData( elem, name, pvt ) {//元素不支持添加屬性的直接返回if ( !jQuery.acceptData( elem ) ) {return;}var i, l, thisCache,isNode = elem.nodeType,//詳細信息查看jQuery.data源碼cache = isNode ? jQuery.cache : elem,id = isNode ? elem[ jQuery.expando ] : jQuery.expando;//緩存中沒有數(shù)據(jù)直接返回if ( !cache[ id ] ) {return;}if ( name ) {//pvt內(nèi)部使用,默認為false(undefined)thisCache = pvt ? cache[ id ] : cache[ id ].data;//緩存對象if ( thisCache ) {//name為字串if ( !jQuery.isArray( name ) ) {// name就是一個keyif ( name in thisCache ) {name = [ name ];} else {//處理ie兼容,如果name是空格相連的字符串,使用split分割name = jQuery.camelCase( name );if ( name in thisCache ) {name = [ name ];} else {name = name.split(" ");}}//name為數(shù)組} else {//如果name是key的數(shù)組// 當數(shù)據(jù)初始化完成,通過("key", "val")簽名,// keys將轉(zhuǎn)化成駱駝寫法.// 如果沒有辦法告知_how_這種key被添加,移除原始類型的key和駱駝寫法的keyname = name.concat( jQuery.map( name, jQuery.camelCase ) );}for ( i = 0, l = name.length; i < l; i++ ) {delete thisCache[ name[i] ];}//如果緩存不為空則返回,如果為空則我們在后面通過delete刪除該對象if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {return;}}}//詳細信息查看jQuery.data源碼if ( !pvt ) {delete cache[ id ].data;//cache不為空則返回if ( !isEmptyDataObject( cache[ id ] ) ) {return;}}//走到這一步表示cache中沒有數(shù)據(jù)了,銷毀cacheif ( isNode ) {jQuery.cleanData( [ elem ], true );// 當支持刪除expandos 或'cache'不是window (#10080)} else if ( jQuery.support.deleteExpando || cache != cache.window ) {delete cache[ id ];// 當所有都失敗時,處理為null} else {cache[ id ] = null;}}

?

?  如果覺得本文不錯,請點擊右下方【推薦】!

轉(zhuǎn)載于:https://www.cnblogs.com/chuaWeb/p/jQuery-1-9-1-cache.html

總結(jié)

以上是生活随笔為你收集整理的jQuery-1.9.1源码分析系列(四) 缓存系统的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。