图示 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

至于这个设计的优劣,又是另外一个话题了,你们能够讨论。

相关文章
相关标签/搜索