摘要:的緩存機(jī)制來看看中提高的數(shù)據(jù)緩存機(jī)制,有兩個函數(shù),分別是和,可以看出來,一個是在對象上,一個是在生成的對象上。而且從源碼來看,的緩存機(jī)制自帶清內(nèi)存操作,更是錦上添花呀。參考源碼分析數(shù)據(jù)緩存本文在上的源碼地址,歡迎來。
歡迎來我的專欄查看系列文章。
不打算介紹 deferred,或者放到后面以后去介紹,因為我對于 js 的異步存在著恐懼,看了半天代碼,發(fā)現(xiàn),用挺好用的,一看源碼,全傻眼了。如果你感興趣,這邊鏈接1,鏈接2。
數(shù)據(jù)緩存jQuery 最初以便捷 DOM 操作而流行,而 DOM 的本質(zhì)其實就是對象,開發(fā)者們又習(xí)慣性的將一些標(biāo)志直接扔給 DOM 本事,這會帶來內(nèi)存泄漏的問題。
比如對于斐波那契數(shù)列,方法可以有遞歸,有迭代,如果用 js 來寫的話有一個比較有意思的方法,就是用緩存來實現(xiàn):
function fib(n){ if(n == 1 || n == 0) return n; if(!fib[n-1]){ fib[n-1] = fib(n-1); } if(!fib[n-2]){ fib[n-2] = fib(n-2); } return fib[n-1] + fib[n-2]; }
因為 fib 不僅是函數(shù),而且是對象,JS 中萬物都是對象,所以才有了這種緩存的解決辦法,這就是前面所說的,不過是在 DOM 上實現(xiàn)的。
當(dāng)然這種方法也會有弊端,造成內(nèi)存泄漏。現(xiàn)代的瀏覽器有自動回收內(nèi)存的機(jī)制,但當(dāng)出現(xiàn)循環(huán)引用或閉包的時候,就會產(chǎn)生內(nèi)存泄漏問題。
就不深入去討論了。
jQuery 的緩存機(jī)制來看看 jQuery 中提高的數(shù)據(jù)緩存機(jī)制,有兩個函數(shù),分別是 jQuery.data()和 jQuery.fn.data(),可以看出來,一個是在 jQuery 對象上,一個是在 jQuery 生成的對象上。如果仔細(xì)閱讀的話,你會發(fā)現(xiàn) jQuery 中很多函數(shù)都有兩個,原型上一個,jQuery 上一個。
jQuery.data() 有兩種使用,一個用于綁定,一個用于查詢:
jQuery.data( element, key, value )
jQuery.data( element, key )
上面的 element 參數(shù)表示 DOM 元素,比如一個例子如下:
jQuery.data(document.body, "foo", 52); jQuery.data(document.body, "bar", "test"); jQuery.data(document.body, "foo"); // 52 jQuery.data(document.body, "bar"); // "test"
還有 .data() 方法,.data(),這個函數(shù)就直接在 jquery 對象上實行綁定 data:
$("body").data("foo", 52); $("body").data("bar", { myType: "test", count: 40 }); $("body").data({ baz: [ 1, 2, 3 ] }); $("body").data("foo"); // 52 $("body").data(); // { foo: 52, bar: { myType: "test", count: 40 }, baz: [ 1, 2, 3 ] }
這邊有一個小細(xì)節(jié)數(shù)據(jù)緩存接口:
var jq1 = $("body"); var jq2 = $("body"); jq1.data("a", 1); jq2.data("a", 2); jq1.data("a"); //2 jq2.data("a"); //2 // 數(shù)據(jù)被覆蓋 $.data(jq1, "b", 3); $.data(jq2, "b", 4); $.data(jq1, "b"); //3 $.data(jq2, "b"); //4 // 不會被覆蓋
可以看出來,通過這兩種方法綁定的數(shù)據(jù),其實是不一樣的,前者會被覆蓋,而后者不會,說明在 cache 中肯定有某種神秘的力量將他們區(qū)別開來。
源碼在 jQuery 中的源碼,大致是這樣的結(jié)構(gòu):
function Data(){...} Data.prototype = { cache: function(){...}, set: function(){...}, get: function(){...}, access: function(){...}, remove: function(){...}, hasData: function(){...} } var dataUser = new Data(); jQuery.extend({ data: function( elem, name, data ) { return dataUser.access( elem, name, data ); }, hasData: function( elem ) { return dataUser.hasData( elem ) || dataPriv.hasData( elem ); }, removeData: function( elem, name ) { dataUser.remove( elem, name ); } }) jQuery.fn.extend({ data: function(){ ... dataUser... ... }, removeData: function(){...} })
由于之前已經(jīng)弄懂 jQuery 內(nèi)部結(jié)構(gòu),對于這個一點也不驚訝,在 jQuery 和 jQuery 的原型上分別有一個 data 函數(shù),用來處理各自的情況。
既然已經(jīng)知道了 data 的基本結(jié)構(gòu),我們來各個擊破,先來看一下 function Data():
function Data() { // jQuery.expando 是 jQuery 的標(biāo)識 this.expando = jQuery.expando + Data.uid++; } Data.uid = 1; jQuery.expando = ("3.1.1" + Math.random()).replace( /D/g, "" ) // "3.1.10.9610206515567563".replace( /D/g, "" ) // "31109610206515567563"
接著是 prototype:
Data.prototype = { // 建立一個 cache cache: function( owner ) { // Check if the owner object already has a cache var value = owner[ this.expando ]; // If not, create one if ( !value ) { value = {}; // We can accept data for non-element nodes in modern browsers, // but we should not, see #8335. // Always return an empty object. if ( acceptData( owner ) ) { // 判斷 owner 是一個合格者后 if ( owner.nodeType ) { owner[ this.expando ] = value; // Otherwise secure it in a non-enumerable property // configurable must be true to allow the property to be // deleted when data is removed } else { Object.defineProperty( owner, this.expando, { value: value, configurable: true } ); } } } return value; }, // set 函數(shù)就是為 dom 設(shè)置 key,value set: function( owner, data, value ) { var prop, cache = this.cache( owner ); if ( typeof data === "string" ) { cache[ jQuery.camelCase( data ) ] = value; // 處理 data 為這種情況: [ owner, { properties } ] } else { // Copy the properties one-by-one to the cache object for ( prop in data ) { cache[ jQuery.camelCase( prop ) ] = data[ prop ]; } } return cache; }, get: function( owner, key ) { return key === undefined ? this.cache( owner ) : // Always use camelCase key (gh-2257) owner[ this.expando ] && owner[ this.expando ][ jQuery.camelCase( key ) ]; }, // 用來訪問,將 get、set 結(jié)合到一起,并對 underfined 判斷 access: function( owner, key, value ) { if ( key === undefined || ( ( key && typeof key === "string" ) && value === undefined ) ) { return this.get( owner, key ); } this.set( owner, key, value ); return value !== undefined ? value : key; }, // 用于移除 cache remove: function( owner, key ) { var i, cache = owner[ this.expando ]; if ( cache === undefined ) { return; } if ( key !== undefined ) { // 支持刪除數(shù)組格式的 key if ( jQuery.isArray( key ) ) { key = key.map( jQuery.camelCase ); } else { key = jQuery.camelCase( key ); // 為了保持一致,強行的構(gòu)造了一個 數(shù)組 key = key in cache ? [ key ] : ( key.match( rnothtmlwhite ) || [] ); } i = key.length; // 刪 while ( i-- ) { delete cache[ key[ i ] ]; } } // cache 為空的時候,刪除整個緩存 if ( key === undefined || jQuery.isEmptyObject( cache ) ) { if ( owner.nodeType ) { owner[ this.expando ] = undefined; } else { delete owner[ this.expando ]; } } }, hasData: function( owner ) { var cache = owner[ this.expando ]; return cache !== undefined && !jQuery.isEmptyObject( cache ); } };
然后是 jQuery.data():
var dataPriv = new Data(); //以后會講到 var dataUser = new Data(); jQuery.extend( { hasData: function( elem ) { return dataUser.hasData( elem ) || dataPriv.hasData( elem ); }, data: function( elem, name, data ) { return dataUser.access( elem, name, data ); }, removeData: function( elem, name ) { dataUser.remove( elem, name ); }, // TODO: Now that all calls to _data and _removeData have been replaced // with direct calls to dataPriv methods, these can be deprecated. _data: function( elem, name, data ) { return dataPriv.access( elem, name, data ); }, _removeData: function( elem, name ) { dataPriv.remove( elem, name ); } } );
源碼里面有 dataPriv 和 dataUser,作者做了一個 TODO 標(biāo)記,
接著是 jQuery.fn.data():
jQuery.fn.extend( { data: function( key, value ) { var i, name, data, // 將第一個 dom 賦給 elem elem = this[ 0 ], attrs = elem && elem.attributes; // key 為 underfined,表示參數(shù)空,獲取全部 if ( key === undefined ) { if ( this.length ) { data = dataUser.get( elem ); if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { i = attrs.length; while ( i-- ) { // 這里面從 dom 的 attribute 中搜索 data- 開通的屬性 if ( attrs[ i ] ) { name = attrs[ i ].name; if ( name.indexOf( "data-" ) === 0 ) { name = jQuery.camelCase( name.slice( 5 ) ); dataAttr( elem, name, data[ name ] ); } } } dataPriv.set( elem, "hasDataAttrs", true ); } } return data; } // object 類型 if ( typeof key === "object" ) { return this.each( function() { dataUser.set( this, key ); } ); } // key value 的情況,利用 access 函數(shù) return access( this, function( value ) { var data; // The calling jQuery object (element matches) is not empty // (and therefore has an element appears at this[ 0 ]) and the // `value` parameter was not undefined. An empty jQuery object // will result in `undefined` for elem = this[ 0 ] which will // throw an exception if an attempt to read a data cache is made. if ( elem && value === undefined ) { // Attempt to get data from the cache // The key will always be camelCased in Data data = dataUser.get( elem, key ); if ( data !== undefined ) { return data; } // Attempt to "discover" the data in // HTML5 custom data-* attrs data = dataAttr( elem, key ); if ( data !== undefined ) { return data; } // We tried really hard, but the data doesn"t exist. return; } // Set the data... this.each( function() { // We always store the camelCased key dataUser.set( this, key, value ); } ); }, null, value, arguments.length > 1, null, true ); }, removeData: function( key ) { return this.each( function() { dataUser.remove( this, key ); } ); } } );
data 函數(shù)略有不同,但思路也很清晰。
有幾個要提一下的函數(shù)其中,有幾個函數(shù),也來介紹一下,acceptData:
var acceptData = function( owner ) { // Accepts only: // - Node // - Node.ELEMENT_NODE // - Node.DOCUMENT_NODE // - Object // - Any return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); };
acceptData 是判斷 owner 的類型,具體關(guān)于 nodeType,去看看這里吧。
jQuery.camelCase:
jQuery.camelCase = function (string) { return string.replace(/^-ms-/, "ms-").replace(/-([a-z])/g, function (all, letter) { return letter.toUpperCase(); }); }
這個函數(shù)就是做了一些特殊字符串的 replace,具體有啥用,我也不是很清楚。
isEmptyObject 是判斷一個 Object 是否為空的函數(shù),挺有意思的,可以借鑒:
jQuery.isEmptyObject = function (obj) { var name; for (name in obj) { return false; } return true; }
dataAttr 是一個從 DOM 中搜索以 data- 開頭屬性的函數(shù):
function dataAttr( elem, key, data ) { var name; // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute if ( data === undefined && elem.nodeType === 1 ) { name = "data-" + key.replace( /[A-Z]/g, "-$&" ).toLowerCase(); // 利用 dom 自身的 get 操作 data = elem.getAttribute( name ); if ( typeof data === "string" ) { try { // 先看有沒有 data = getData( data ); } catch ( e ) {} // Make sure we set the data so it isn"t changed later dataUser.set( elem, key, data ); } else { data = undefined; } } return data; }總結(jié)
jQuery 的 data 緩存從源碼來看的話,真的不是很難,而且不難發(fā)現(xiàn),jQuery 緩存的實質(zhì),其實就是在內(nèi)部先弄一個 Object,然后和緩存體(DOM)建立一對一的聯(lián)系,所有增刪改查的操作,都是圍繞著 jQuery 內(nèi)部來的,不直接對 DOM 操作,這樣就可以避免內(nèi)存泄漏。而且從源碼來看,jQuery 的緩存機(jī)制自帶清內(nèi)存操作,更是錦上添花呀。
參考jQuery 2.0.3 源碼分析 數(shù)據(jù)緩存
jQuery.data()
本文在 github 上的源碼地址,歡迎來 star。
歡迎來我的博客交流。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/81642.html
摘要:十的觸發(fā)機(jī)制被點擊了元素本身綁定了一個事件,但是是原生事件,它是靠綁定來觸發(fā)事件的。 showImg(https://segmentfault.com/img/remote/1460000019505402); 前言:最重要的還是最后的流程圖,可以試著根據(jù)流程圖手寫實現(xiàn)$().on(),下篇文章會放出模擬實現(xiàn)的代碼。 一、舉例 這是A 這是C ...
摘要:不過也有自己的一套自定義事件方案??梢院褪录脕韺Ρ龋麄兌际怯脕砟M和執(zhí)行監(jiān)聽的事件。冒泡事件就是就是由內(nèi)向外冒泡的過程,這個過程不是很復(fù)雜。參考解密事件核心自定義設(shè)計三解密事件核心模擬事件四本文在上的源碼地址,歡迎來。 歡迎來我的專欄查看系列文章。 以前,我只知道,只有當(dāng)對瀏覽器中的元素進(jìn)行點擊的時候,才會出發(fā) click 事件,其它的事件也一樣,需要人為的鼠標(biāo)操作。 showIm...
摘要:作為此時不存在,直接從數(shù)據(jù)緩存中獲取并返回。作用是觸發(fā)中的回調(diào)函數(shù),的表示只讓觸發(fā)一次后,就需要清理,表示是將清空成空數(shù)組還是空字符。 showImg(https://segmentfault.com/img/remote/1460000019558449); 前言:queue()方法和dequeue()方法是為 jQuery 的動畫服務(wù)的,目的是為了允許一系列動畫函數(shù)被異步調(diào)用,但不...
閱讀 964·2021-11-24 10:42
閱讀 3475·2021-11-19 11:34
閱讀 2605·2021-09-29 09:35
閱讀 2525·2021-09-09 09:33
閱讀 642·2021-07-26 23:38
閱讀 2515·2019-08-30 10:48
閱讀 1385·2019-08-28 18:07
閱讀 422·2019-08-26 13:44