国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

jQuery 源碼系列(八)data 緩存機(jī)制

shinezejian / 468人閱讀

摘要:的緩存機(jī)制來看看中提高的數(shù)據(jù)緩存機(jī)制,有兩個(gè)函數(shù),分別是和,可以看出來,一個(gè)是在對(duì)象上,一個(gè)是在生成的對(duì)象上。而且從源碼來看,的緩存機(jī)制自帶清內(nèi)存操作,更是錦上添花呀。參考源碼分析數(shù)據(jù)緩存本文在上的源碼地址,歡迎來。

歡迎來我的專欄查看系列文章。

不打算介紹 deferred,或者放到后面以后去介紹,因?yàn)槲覍?duì)于 js 的異步存在著恐懼,看了半天代碼,發(fā)現(xiàn),用挺好用的,一看源碼,全傻眼了。如果你感興趣,這邊鏈接1,鏈接2。

數(shù)據(jù)緩存

jQuery 最初以便捷 DOM 操作而流行,而 DOM 的本質(zhì)其實(shí)就是對(duì)象,開發(fā)者們又習(xí)慣性的將一些標(biāo)志直接扔給 DOM 本事,這會(huì)帶來內(nèi)存泄漏的問題。

比如對(duì)于斐波那契數(shù)列,方法可以有遞歸,有迭代,如果用 js 來寫的話有一個(gè)比較有意思的方法,就是用緩存來實(shí)現(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];
}

因?yàn)?fib 不僅是函數(shù),而且是對(duì)象,JS 中萬物都是對(duì)象,所以才有了這種緩存的解決辦法,這就是前面所說的,不過是在 DOM 上實(shí)現(xiàn)的。

當(dāng)然這種方法也會(huì)有弊端,造成內(nèi)存泄漏。現(xiàn)代的瀏覽器有自動(dòng)回收內(nèi)存的機(jī)制,但當(dāng)出現(xiàn)循環(huán)引用或閉包的時(shí)候,就會(huì)產(chǎn)生內(nèi)存泄漏問題

就不深入去討論了。

jQuery 的緩存機(jī)制

來看看 jQuery 中提高的數(shù)據(jù)緩存機(jī)制,有兩個(gè)函數(shù),分別是 jQuery.data()jQuery.fn.data(),可以看出來,一個(gè)是在 jQuery 對(duì)象上,一個(gè)是在 jQuery 生成的對(duì)象上。如果仔細(xì)閱讀的話,你會(huì)發(fā)現(xiàn) jQuery 中很多函數(shù)都有兩個(gè),原型上一個(gè),jQuery 上一個(gè)。

jQuery.data() 有兩種使用,一個(gè)用于綁定,一個(gè)用于查詢:

jQuery.data( element, key, value )

jQuery.data( element, key )

上面的 element 參數(shù)表示 DOM 元素,比如一個(gè)例子如下:

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(),這個(gè)函數(shù)就直接在 jquery 對(duì)象上實(shí)行綁定 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 ] }

這邊有一個(gè)小細(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
// 不會(huì)被覆蓋

可以看出來,通過這兩種方法綁定的數(shù)據(jù),其實(shí)是不一樣的,前者會(huì)被覆蓋,而后者不會(huì),說明在 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),對(duì)于這個(gè)一點(diǎn)也不驚訝,在 jQuery 和 jQuery 的原型上分別有一個(gè) data 函數(shù),用來處理各自的情況。

既然已經(jīng)知道了 data 的基本結(jié)構(gòu),我們來各個(gè)擊破,先來看一下 function Data()

function Data() {
  // jQuery.expando 是 jQuery 的標(biāo)識(shí)
  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 = {
  // 建立一個(gè) 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 是一個(gè)合格者后
        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é)合到一起,并對(duì) 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 );
        // 為了保持一致,強(qiáng)行的構(gòu)造了一個(gè) 數(shù)組
        key = key in cache ?
          [ key ] :
          ( key.match( rnothtmlwhite ) || [] );
      }
      i = key.length;
      // 刪
      while ( i-- ) {
        delete cache[ key[ i ] ];
      }
    }
    // cache 為空的時(shí)候,刪除整個(gè)緩存
    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(); //以后會(huì)講到
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,作者做了一個(gè) TODO 標(biāo)記,

接著是 jQuery.fn.data()

jQuery.fn.extend( {
  data: function( key, value ) {
    var i, name, data,
      // 將第一個(gè) 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ù)略有不同,但思路也很清晰。

有幾個(gè)要提一下的函數(shù)

其中,有幾個(gè)函數(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();
  });
}

這個(gè)函數(shù)就是做了一些特殊字符串的 replace,具體有啥用,我也不是很清楚。

isEmptyObject 是判斷一個(gè) Object 是否為空的函數(shù),挺有意思的,可以借鑒:

jQuery.isEmptyObject = function (obj) {
    var name;
    for (name in obj) {
        return false;
    }
    return true;
}

dataAttr 是一個(gè)從 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 緩存的實(shí)質(zhì),其實(shí)就是在內(nèi)部先弄一個(gè) Object,然后和緩存體(DOM)建立一對(duì)一的聯(lián)系,所有增刪改查的操作,都是圍繞著 jQuery 內(nèi)部來的,不直接對(duì) DOM 操作,這樣就可以避免內(nèi)存泄漏。而且從源碼來看,jQuery 的緩存機(jī)制自帶清內(nèi)存操作,更是錦上添花呀。

參考

jQuery 2.0.3 源碼分析 數(shù)據(jù)緩存

jQuery.data()

本文在 github 上的源碼地址,歡迎來 star。

歡迎來我的博客交流。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/81642.html

相關(guān)文章

  • jQuery之事件綁定到觸發(fā)全過程及知識(shí)點(diǎn)補(bǔ)充

    摘要:十的觸發(fā)機(jī)制被點(diǎn)擊了元素本身綁定了一個(gè)事件,但是是原生事件,它是靠綁定來觸發(fā)事件的。 showImg(https://segmentfault.com/img/remote/1460000019505402); 前言:最重要的還是最后的流程圖,可以試著根據(jù)流程圖手寫實(shí)現(xiàn)$().on(),下篇文章會(huì)放出模擬實(shí)現(xiàn)的代碼。 一、舉例 這是A 這是C ...

    Jioby 評(píng)論0 收藏0
  • jQuery 源碼系列(十四)自定義事件

    摘要:不過也有自己的一套自定義事件方案。可以和事件拿來對(duì)比,他們都是用來模擬和執(zhí)行監(jiān)聽的事件。冒泡事件就是就是由內(nèi)向外冒泡的過程,這個(gè)過程不是很復(fù)雜。參考解密事件核心自定義設(shè)計(jì)三解密事件核心模擬事件四本文在上的源碼地址,歡迎來。 歡迎來我的專欄查看系列文章。 以前,我只知道,只有當(dāng)對(duì)瀏覽器中的元素進(jìn)行點(diǎn)擊的時(shí)候,才會(huì)出發(fā) click 事件,其它的事件也一樣,需要人為的鼠標(biāo)操作。 showIm...

    elliott_hu 評(píng)論0 收藏0
  • jQuery源碼解析之$.queue()、$.dequeue()和jQuery.Callbacks(

    摘要:作為此時(shí)不存在,直接從數(shù)據(jù)緩存中獲取并返回。作用是觸發(fā)中的回調(diào)函數(shù),的表示只讓觸發(fā)一次后,就需要清理,表示是將清空成空數(shù)組還是空字符。 showImg(https://segmentfault.com/img/remote/1460000019558449); 前言:queue()方法和dequeue()方法是為 jQuery 的動(dòng)畫服務(wù)的,目的是為了允許一系列動(dòng)畫函數(shù)被異步調(diào)用,但不...

    itvincent 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<