Zepto源碼之輔助函數

用於判斷類型的內部函數

// class2type對象與toString方法的定義
class2type = {}
toString = class2type.toString

// 調用 $.each 給 class2type 對象設置屬性與參數,裏面有全部常用的參數類型
// PS:說多一句,ES6中新增Symbol類型
$.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function (i, name) {
    class2type["[object " + name + "]"] = name.toLowerCase()
})
/*
    class2type = {
        "[object Boolean]": "boolean",
        "[object Number]": "number",
        "[object String]": "string",
        "[object Function]": "function",
        "[object Array]": "array",
        "[object Date]": "date",
        "[object RegExp]": "regexp",
        "[object Object]": "object",
        "[object Error]": "error"
    }
*/

// 用來查看傳入參數的對象類型
// 如果傳入參數爲 undefined 或 null ,那麼 obj == null 的結果爲false,直接返回 "null" 或者 "undefined"
// 這是因爲隱式類型轉換,undefined,null在進行判斷的時候都傳統隱式轉化爲false,這也是爲什麼使用 == 而不是更安全的 === 的原因
// 如果不爲undefined或者null那麼就根據 class2Type 對象來確定類型,如果沒有找到類型,就默認其是object類型
function type(obj) {
    return obj == null ? String(obj) : class2type[toString.call(obj)] || "object"
}

// 判斷對象是否爲window對象
function isWindow(obj) { return obj != null && obj == obj.window }

// 調用內部方法 type 來判斷參數類型,並判斷其是否爲對象
function isObject(obj) { return type(obj) == "object" }

// 判斷參數是否爲函數
function isFunction(value) { return type(value) == "function" }

// 判斷參數是否爲document對象
function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE }

// 判斷對象類型是不是純粹的對象,具體iswg其是不是window對象與是否有原型
function isPlainObject(obj) { return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype }

// 判斷對象是否爲數組
isArray = Array.isArray || function (object) { return object instanceof Array }

// 判斷參數是不是一個類似於數組的對象
// 如:{ "1":1,"2":2,length:2}
function likeArray(obj) {
    // 判斷參數是否存在,判斷參數中是否存在length屬性,存在的話,length的值爲參數length屬性的值,否則爲undefined
    var length = !!obj && 'length' in obj && obj.length,
        type = $.type(obj)

    // 如果是funtion或者windows對象,那麼肯定不會是類數組,直接返回false
    // 如果 array ,那麼直接返回 true
    // 如果不是 array ,且 length 爲 0。那麼只能認定這是一個類空數組了,返回true
    // 如果是上述示例中的形式的類數組,那麼可以看作數組,且除了數組屬性,可以像數組一樣使用,返回true
    return 'function' != type && !isWindow(obj) && (
      'array' == type || length === 0 ||
      (typeof length == 'number' && length > 0 && (length - 1) in obj)
    )
  }

數組方法

// 在一開始就定義了一個空數組,然後把數組的原型上的方法賦給 concat, filter, slice
emptyArray = []
concat = emptyArray.concat
filter = emptyArray.filter
slice = emptyArray.slice


/*
 * 這是一個過濾器方法,用於除去數組中的 null 與 undefined
 * 這麼看可能利於理解
 * function compact (array) {
 *   return array.filter( (el) => item != null )  
 * }
 */
function compact(array) { return filter.call(array, function (item) { return item != null }) }

// 將數組展開,但是隻能展開一層
function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array }

/**
 * 將數組去重
 * 在ES6中可以這麼寫,更加的簡單
 * uniq = (arr) => [...new Set(arr)]
 **/
uniq = function (array) { return filter.call(array, function (item, idx) { return array.indexOf(item) == idx }) }

字符串方法


// 把HTML標準中定義的 data-proto-name 形式的內容轉換成對 js 安全的駝峯式命名,主要是消除 - 的影響
camelize = function (str) { return str.replace(/-+(.)?/g, function (match, chr) { return chr ? chr.toUpperCase() : '' }) }


// 將駝峯式寫法轉換成 _ 連字符形式,並將 ::  轉化成 /a
function dasherize(str) {
    return str.replace(/::/g, '/')
              .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
              .replace(/([a-z\d])([A-Z])/g, '$1_$2')
              .replace(/_/g, '-')
              .toLowerCase()
}

內部通用方法


// 值反序列化
function deserializeValue(value) {
    try {
      return value ?
        value == "true" ||
        ( value == "false" ? false :
          value == "null" ? null :
          +value + "" == value ? +value :
          /^[\[\{]/.test(value) ? $.parseJSON(value) :
          value )
        : value
    } catch(e) {
      return value
    }
  }

  // 我們可能用一種簡單的方式來改寫一下,畢竟多層嵌套的三目運算符理解上容易出問題
  function deserializeValue(value) {
      // 主要是用於處理一些無法反序列化的值,不能處理,爲了安全,直接返回其原值
      try{
          // 如果 value 存在值,這裏主要針對 null, false, undefined, 0, -0, NaN, '' 等值,這些值在隱式類型轉換中都會被當作false
          if (value){
              // 檢測是否是true
              if(value === 'true') {
                  return true
              } else if(value === 'false') {
              // 檢測是否是false
                  return false
              } else {
                  // 檢測是否是null
                  if(value === 'null') {
                      return null
                  } else {
                     // 檢測是否是數字
                     if(Number(value) + '' === value){
                         return parseFloat(value)
                     } else {
                         // 檢測是否是JSON
                         if(/^[\[\{]/.test(value)) {
                             return $.parseJSON(value)
                         } else {
                             return value
                         }
                     }
                  }
              }
          } else {
          // 這個意思是,啥都沒檢出來,大哥我服,您先回去
              return value
          }
      } catch(e) {
          return value
      }
  }

zepto.matches = function(element, selector) {
    // 如果選擇器不存在或者元素不存在或者元素不爲元素節點那麼直接返回 false
    if (!selector || !element || element.nodeType !== 1) return false
    // 使用polyfill方法來拿到 matchesSelector ,這樣可以在下面使用統一內容
    var matchesSelector = element.matches || element.webkitMatchesSelector ||
                          element.mozMatchesSelector || element.oMatchesSelector ||
                          element.matchesSelector
    // 如果元素選擇器存在,那麼直接調用元素選擇來選擇元素並返回
    if (matchesSelector) return matchesSelector.call(element, selector)


    var match, parent = element.parentNode, temp = !parent
    if (temp) (parent = tempParent).appendChild(element)
    match = ~zepto.qsa(parent, selector).indexOf(element)
    temp && tempParent.removeChild(element)
    return match
  }

// zepto的css選擇器
zepto.qsa = function(element, selector){
    var found,
        // 如果選擇器第一個元素是 # 那麼說明是 id 選擇器,maybeId 爲 true    
        maybeID = selector[0] == '#',
        // 如果不是 id 選擇器,且第一位爲 . 那麼說明是類選擇器,maybeClass 爲 true
        maybeClass = !maybeID && selector[0] == '.',
        // 將選擇器前面的 # 或者 . 去掉
        nameOnly = maybeID || maybeClass ? selector.slice(1) : selector,
        // simpleSelectorRE = /^[\w-]*$/,
        // 檢驗是不是單個選擇器
        isSimple = simpleSelectorRE.test(nameOnly)
    return (element.getElementById && isSimple && maybeID) ?
      ( (found = element.getElementById(nameOnly)) ? [found] : [] ) :
      (element.nodeType !== 1 && element.nodeType !== 9 && element.nodeType !== 11) ? [] :
      slice.call(
        isSimple && !maybeID && element.getElementsByClassName ?
          maybeClass ? element.getElementsByClassName(nameOnly) :
          element.getElementsByTagName(selector) :
          element.querySelectorAll(selector)
      )
      // return 部分比較複雜,我們寫成簡單的形式吧
      /*           start                   */
      // 如果 getElementById 方法存在且是id選擇器同時還是一個簡單的選擇器
      if(element.getElementById && isSimple && maybeId) {
      // 使用 getElementById 來查找,如果找到就返回內容數組,找不到返回一個空數組
          found = element.getElementById(nameOnly)
          if(found === undefined) {
              return []
          } else {
              return [found]
          }
      } else {

          if(element.nodeType !== 1 && element.nodeType !== 9 && element.nodeType !== 11){
          // 如果傳用元素不是元素節點,文檔,輕量級文檔對象那也不用費什麼勁了,找不到,直接返回一個空數組吧
              return []
          } else {
              if(isSimple && !maybeID && element.getElementsByClassName){
              // 如果是簡單選擇器且不是 id 選擇器,瀏覽器支持 getElementsByClassName 方法
                  if(maybeClass){
                  // 如果是傳入選擇器是類選擇器,那麼用類選擇器篩選,並將獲取內容轉化成一個數組對象傳出
                       found = element.getElementsByClassName(nameOnly)
                       return slice.call(found)
                  } else {
                   // 如果是傳入選擇器是不是類選擇器,那麼用標籤選擇器篩選,並將獲取內容轉化成一個數組對象傳出
                      found = element.getElementsByTagName(selector)
                      return slice.call(found)
                  }
              } else {
              // 使用 querySelectorAll 方法篩選選擇器內容,並將內容轉化成一個數組對象傳出
                  found = element.querySelectorAll(selector)
                  return slice.call(found)
              }
          }
      }
      /*           end                     */
  }

// 過濾函數
function filtered(nodes, selector) {
    // 如果不存在選擇器,那麼直接把nodes 轉化成 zepto 對象返回,否則轉化成 zepto 對象後再用 filter 過濾
    return selector == null ? $(nodes) : $(nodes).filter(selector)
  }


zepto.uniq = uniq
zepto.deserializeValue = deserializeValue
發佈了88 篇原創文章 · 獲贊 41 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章