图示 Zepto 源码结构

关于 Zepto 源码结构分析的文章已经很多了,本文主要从两点,即图示详细步骤跟踪上,对其进行分析。

首先还是上源码:

外层结构源码

var Zepto = (function() {

})()
window.Zepto = Zepto
window.$ === undefined && (window.$ = Zepto)
复制代码

外层结构很简单,无非是执行一个立即执行函数,把返回结构赋给 Zepto,再把 Zepto 绑定到全局。最后,如果全局的美元符号 $ 没有被占用,则把 Zepto 赋给 $。很多文章里都已经讨论过了,不再细说。

核心结构源码

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 $复制代码

以上代码是抽取主要部分后得到的骨干,饶是如此,结构就已经很复杂了。对象和原型之间互相赋值和引用,函数之间互相调用,楞一遍看下来让人非常晕。

我们现在就来整理一下。

首先,这里有三个最主要的对象,这三个对象就是:

$, zepto, Z。注意,这里的 zepto 首字母是小写,不要和外层结构的 Zepto 搞混了。

下面就对这三者之间的关系进行分析。


$, zepto, Z 关系分析

仔细看源码,分析三者以及它们之间的关系,我们发现以下几个事实:

  • 内部结构最终返回的是 $ ,也就是说其实就是 $ 被赋给了外层的 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 之间的关系应该就清晰多了。

至于图中出现的 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

至于这个设计的优劣,又是另一个话题了,大家可以讨论。


转载于:https://juejin.im/post/59db0a45f265da066c2344e5

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值