圖示 Zepto 源碼結構

關於 Zepto 源碼結構分析的文章已經不少了,本文主要從兩點,即圖示詳細步驟跟蹤上,對其進行分析。javascript

首先仍是上源碼:html

外層結構源碼

var Zepto = (function() {

})()
window.Zepto = Zepto
window.$ === undefined && (window.$ = Zepto)
複製代碼

外層結構很簡單,無非是執行一個當即執行函數,把返回結構賦給 Zepto,再把 Zepto 綁定到全局。最後,若是全局的美圓符號 $ 沒有被佔用,則把 Zepto 賦給 $。不少文章裏都已經討論過了,再也不細說。java

核心結構源碼

var zepto = {}, $

function Z(doms) {
  var len = doms.length 
  for (var i = 0; i < len; i++) {
    this[i] = doms[i]
  }
  this.length = doms.length
}

zepto.fragment = function(html, name, properties) {
  // 根據 html 字符串生成 dom
}

zepto.isZ = function(object) {  return object instanceof zepto.Z}
zepto.Z = function(doms) {
  return new Z(doms)
}

zepto.init = function(selector, context) {
  var dom
  // 根據 selector 生成 dom
  // ···
  return zepto.Z(dom, selector)}

$ = function(selector, context){  
  return zepto.init(selector, context)
}

zepto.qsa = function(element, selector){  
  // 根據 選擇器字符串生成 dom
}

$.type = type
  ······
  // 把各類工具方法綁定到 $ 上

$.fn = {  constructor: zepto.Z,
  forEach: emptyArray.forEach  ······
  // 把各類實例方法綁定到 $.fn 上
}

zepto.Z.prototype = Z.prototype = $.fn

$.zepto = zepto

return $複製代碼

以上代碼是抽取主要部分後獲得的骨幹,饒是如此,結構就已經很複雜了。對象和原型之間互相賦值和引用,函數之間互相調用,楞一遍看下來讓人很是暈。數組

咱們如今就來整理一下。dom

首先,這裏有三個最主要的對象,這三個對象就是:函數

$, zepto, Z。注意,這裏的 zepto 首字母是小寫,不要和外層結構的 Zepto 搞混了。工具

下面就對這三者之間的關係進行分析。ui


$, zepto, Z 關係分析

仔細看源碼,分析三者以及它們之間的關係,咱們發現如下幾個事實:this

  • 內部結構最終返回的是 $ ,也就是說其實就是 $ 被賦給了外層的 Zepto
  • $ 自己是一個函數,它調用了 zepto.init 方法 並返回。
  • zepto.init 函數作了不少操做,其中最後一步調用了 zepto.Z
  • Z 是一個構造函數。
  • zepto.Z 方法調用構造函數 Z,返回一個 Z 的實例。
  • zepto 對象被賦給了 $.zepto 屬性。
  • $.fn 被賦給了 zepto.ZZ 的原型。也就是說後二者的原型就是 $.fn
好了,看上面這一大坨字到如今,是否是仍是以爲特別暈?不要緊,一圖抵千言,我特地用腦圖工具畫了一張關係圖,請往下看。


結合圖例仔細看幾遍,$, zepto, Z, zepto.Z, zepto.init, $.fn 之間的關係應該就清晰多了。spa

至於圖中出現的 zepto.qsazepto.fragment,那是在 zepto.init 方法中被調用的重要方法,因此一併也畫進去了,對總體結構沒有影響。


執行詳細過程

雖然知道了源碼內部三個主要對象的關係,可是源碼又是如何起做用的呢?

咱們知道,Zepto 使用時都是以選擇器爲開端的。當咱們使用 $()來生成一個 Zepto 對象時,內部究竟發生了什麼?如今咱們就從源碼入手開始分析。

  1. 進入 $ 方法,內部調用 zepto.init 方法。
  2. 進入 zepto.init 方法。該方法判斷傳入的 selector 參數的類型,對其作相應的處理。
    1. 若是是 html 字符串,就調用 zepto.fragment 方法。
    2. 若是是選擇器字符串,就調用 zepto.qsa 方法。
    3. selector 參數是其餘類型時(對象、數組、Zepto 實例、函數······),作其餘的相應處理。
    4. 最終調用 zepto.Z(dom, selector) 對生成的 dom 作一層包裹。
  3. 進入 zepto.Z 方法。該方法經過 new Z(dom, selector) 來生成一個 Z 的實例並返回。
  4. 進入構造函數 ZZ 的內部很簡單,僅僅是對傳入的 dom 作一個簡單的循環拷貝,並複製了它的 length,生成了一個類數組對象。這個對象就是一開始 $() 方法最終返回的對象。

至此,通過層層遞進的分析,咱們獲得告終論:$() 返回的就是一個 Z 的實例。

前面代碼的倒數第三行有 zepto.Z.prototype = Z.prototype = $.fn 。這行代碼很是重要,它讓 Zprototype 指向了 $.fn,這也是爲何 Z 的實例能夠調用 $.fn 中的一大堆方法。

至於這個等式的前半部分,zepto.Z.prototype = Z.prototype,大概是因爲 $.fnconstructor 被指給了zepto.Z。爲了讓 $() instanceof Zepto.zepto.Z 成立,因此纔有這一等式。

不過,我認爲這多少有點違揹人的直覺,也許讓 $.fnconstructor 指向 $ 自己更好。畢竟在 jQuery 中,$() instanceof jQuery === true ,可是在 Zepto 中,$() instanceof Zepto === false

至於這個設計的優劣,又是另外一個話題了,你們能夠討論。

相關文章
相關標籤/搜索