zepto源碼研究 - data.js

簡要:$.fn.data存儲的時候會將value轉化爲字符串而後存儲起來,取出後還得JOSN.parse(),對於dom而言不只增長了自定義屬性,讀取也不方便,不適合爲dom關聯大量數據,這裏的data.js 會在內存中開闢一塊地方專門存放數據,而後在dom上關聯一個能夠指示存放位置的標識。node

源碼以下:json

//     Zepto.js
//     (c) 2010-2016 Thomas Fuchs
//     Zepto.js may be freely distributed under the MIT license.

// The following code is heavily inspired by jQuery's $.fn.data()

;(function($){
  var data = {}, dataAttr = $.fn.data, camelize = $.camelCase,
  /*
     var exp = $.expando = 'Zepto' + (+new Date());
     undefined
     console.log(exp);
     VM147:2 Zepto1473733619898
     undefined
     console.log('Zepto' + (+new Date()));
     VM196:2 Zepto1473733648955
     undefined
     +new Date()  //TODO 前面加一個加號就變成Date.now()了
     1473733657459
     new Date()
     Tue Sep 13 2016 10:27:42 GMT+0800 (CST)
     */
    exp = $.expando = 'Zepto' + (+new Date()), emptyArray = []

  // Get value from node:
  // 1. first try key as given,
  // 2. then try camelized key,
  // 3. fall back to reading "data-*" attribute.
  function getData(node, name) {
    //根據node中的標示來獲取data中的store
    var id = node[exp], store = id && data[id]
    // setData(node)將node中全部的自定義屬性組成store對象返回
    if (name === undefined) return store || setData(node)
    else {
      if (store) {
        // 有則返回,沒有則駝峯化後再返回,
        // 通常寫代碼多是先判斷是否有連字符,有則先駝峯化
        // 這裏的小技巧比較好
        if (name in store) return store[name]
        var camelName = camelize(name)
        if (camelName in store) return store[camelName]
      }
      // 最後若是store裏面沒有,則調用$.fn.data,從節點中獲取
      return dataAttr.call($(node), name)
    }
  }

  // Store value under camelized key on node
  function setData(node, name, value) {
    //根據標識符在data緩存池裏面獲取store對象,而後在store裏面設置鍵值
    //首先是獲取標識符,若是沒有則自動生成一個。
    var id = node[exp] || (node[exp] = ++$.uuid),
      //若是是自動生成的id,則須要先將node中全部的自定義屬性組成對象加入到data緩存中
      store = data[id] || (data[id] = attributeData(node))
    // camelize = $.camelCase  轉換連字符式的字符串爲駝峯式,用於CSS模塊和數據緩存模塊
    if (name !== undefined) store[camelize(name)] = value
    return store
  }

  // Read all "data-*" attributes from a node
  // 將node中全部自定義屬性組成對象返回
  function attributeData(node) {
    var store = {}
    // node.attributes:獲取節點全部屬性
    $.each(node.attributes || emptyArray, function(i, attr){
      if (attr.name.indexOf('data-') == 0)
        store[camelize(attr.name.replace('data-', ''))] =
          // 序列化值  把自定義數據讀出來時作應該的轉換,$.data()方法使用,
          // 若是是對象,則轉化爲json字符串,
          $.zepto.deserializeValue(attr.value)
    })
    return store
  }

  $.fn.data = function(name, value) {
    return value === undefined ?
      // set multiple values via object
      // 若是name是對象,循環setData
      $.isPlainObject(name) ?
        this.each(function(i, node){
          $.each(name, function(key, value){ setData(node, key, value) })
        }) :
        // get value from first element
        (0 in this ? getData(this[0], name) : undefined) :
      // set value on all elements
      this.each(function(){ setData(this, name, value) })
  }


  $.fn.removeData = function(names) {
    // names 可能含有空格,像class同樣
    if (typeof names == 'string') names = names.split(/\s+/)
    return this.each(function(){
      // 獲取對應store
      var id = this[exp], store = id && data[id]
      if (store) $.each(names || store, function(key){
        // 這裏的this就是names中的項,即value,若是names不存在,則所有刪除
        delete store[names ? camelize(this) : key]
      })
    })
  }

  // Generate extended `remove` and `empty` functions
  // 從新封裝remove,empty方法,先清空元素包括子元素所緩存的自定義數據,
  // 而後調用原先的remove和empty方法
  ;['remove', 'empty'].forEach(function(methodName){
    var origFn = $.fn[methodName]
    $.fn[methodName] = function() {
      var elements = this.find('*')
      if (methodName === 'remove') elements = elements.add(this);
      elements.removeData();
      return origFn.call(this)
    }
  })
})(Zepto)

這裏涉及到幾個技巧能夠談一下:數組

1:$.isPlainObject(name):判斷是否爲對象緩存

2:0 in this  判斷是否爲數組dom

3:$.each     在回調函數中的this其實就是value,是數組中的項函數

if (store) $.each(names || store, function(key,value){
        // 這裏的this就是names中的項,即value,若是names不存在,則所有刪除
        delete store[names ? camelize(this) : key]
      })

4:從新封裝$.fn函數,var origFn = $.fn[methodName];$.fn[methodName] = function(){};ui

;['remove', 'empty'].forEach(function(methodName){
    var origFn = $.fn[methodName]
    $.fn[methodName] = function() {
      var elements = this.find('*')
      if (methodName === 'remove') elements = elements.add(this);
      elements.removeData();
      return origFn.call(this)
    }
  })

5:當data.js文件加載完後會生成一個exp變量,這個exp變量就是節點存儲標識的自定義屬性名,而經過這個標識就能找到在數據緩存區中該節點所對應的自定義數據緩存對象。這是存儲對象一個很是高明的方法。this

相關文章
相關標籤/搜索