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

相关文章
相关标签/搜索