简易扒一下zepto的源码

大黑 2019-06-21

zepto源码分析

前言

想比10000多行的jQuery,zepto可谓是短小精悍,作为主要应用在移动端的低配版jQuery,源代码只有大概1500行,代码很容易理解,也写的很巧妙。有可以借鉴的东西。

最外层的闭包

最外层是一个立即执行的函数

(function(global, factory) {
  if (typeof define === 'function' && define.amd)
    define(function() { return factory(global) })
  else
    factory(global)
}(this, function(window){
    var Zepto = (function(){
        //代码写在这里面
        var zepto = {};    //zepto是一个空对象
        var $;
        ...
        $.zepto = zepto;
        return $

    })
    window.Zepto = Zepto;
    window.$ === undefined && (window.$ = Zepto);
}))

最后把$和Zepto作为属性挂在了window对象上,对外部只有$使用,不会污染全局环境。

zepto对象

重点要说一下zepto这个对象,这个对象很重要,贯穿始终。需要注意的是,zepto就是一个对象,而jquery里边是一个类。这是不一样的地方。一些方法会挂在zepto对象上。比如Z()这个方法,一会会重点说。

关于$的解读

接下来说的是zepto中的对象$.

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

$是一个很重要的函数,接收两个参数,selector选择器,和context(可选的)。$也是定于在Zepto函数中,我们看他的代码实际上发现,调用$的时候,其实是调用了zepto对象上的init方法,那么这个init方法又是个什么东西呢?

zepto.init()

init这个方法里边有点复杂。

zepto.init = function(selector, context) {
    var dom
    //如果没有传递参数,返回一个空对象。
    if (!selector) return zepto.Z()
    
    // 如果selector是一个字符串(css选择器)
    else if (typeof selector == 'string') {
      //去掉前后空格
      selector = selector.trim()     
      // 如果是<xx>这样的html代码
      if (selector[0] == '<' && fragmentRE.test(selector))
        dom = zepto.fragment(selector, RegExp.$1, context),
        selector = null
      // 如果有context参数,则在上下文中寻找selector
      else if (context !== undefined) return $(context).find(selector)
      // 如果是一个css表达式,通过qsa方法来寻找元素
      else dom = zepto.qsa(document, selector)
    }
    //如果是一个方法,在ready的时候加载它
    else if (isFunction(selector)) return $(document).ready(selector)
    // 如果是zepto对象,返回自身
    else if (zepto.isZ(selector)) return selector
    else {
      // 如果是一个数组,
      if (isArray(selector)) dom = compact(selector)
      // 如果是一个对象,转成一个数组
      else if (isObject(selector))
        dom = [selector], selector = null
      // 如果是一个html代码,创建之。
      else if (fragmentRE.test(selector))
        dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
      // 如果上下文参数不为空,跟上边一样
      else if (context !== undefined) return $(context).find(selector)
      // 否则在document查找selector
      else dom = zepto.qsa(document, selector)
    }
    // 经过一系列判断来适配不同的情况,反正是有dom了。
    return zepto.Z(dom, selector)
  }

前边经过各种判断,init最后会返回zepto对象的Z方法,那个Z方法看名字应该是类似于构造函数之类的。看一下Z的代码。

zepto.Z()

zepto.Z = function(dom, selector) {
    return new Z(dom, selector)
  }

好吧,这个方法返回了一个Z对象,我们知道用new创建的对象需要构造函数,接下来自然我们要看一下构造函数Z了。

function Z

function Z(dom, selector) {
    var i, len = dom ? dom.length : 0
    for (i = 0; i < len; i++) this[i] = dom[i]
    this.length = len
    this.selector = selector || ''
  }

好了,看到这里,暂时告一段落,我们来分析一波。我们调用$(selector,context)这个方法,经过zepto.init(selector,context)--->zepto.Z(dom,selector)----->new Z(dom,selector).
最后生成一个Z的实例,这个实例有个length属性,代表dom的长度。

既然如此,那么我们是如何在Z这个实例上调用那些各种方法,比如addClass()这类的方法。

$.fn

在zepto中$.fn()是一个简简单单的对象。这个对象是一个空对象。而那些操作dom的方法都是写在这个对象里边。我们大概看一下这个对象。

$.fn = {
    constructor: zepto.Z,
    length:0,
    map:function(){...},
    addClass:function(){...},
    remove:function(){...}
    ....
}
//接下来很重要
zepto.Z.prototype = Z.prototype = $.fn

重要的事情说三遍
zepto.Z.prototype = Z.prototype = $.fn
zepto.Z.prototype = Z.prototype = $.fn
zepto.Z.prototype = Z.prototype = $.fn

其实很简单,把$.fn这个对象挂在了Z的原型对象上,那么new Z()出来的实例就都能访问$.fn上面定义的属性了,所以也就能执行各种dom操作。

至此,zepto的精髓基本讲的差不多了。大概用了1000行代码实现了这个过程,接下来的代码基本是分析了一些事件的问题还有ajax相关的东西。暂且不说了。

相关推荐