大黑 2019-06-21
想比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就是一个对象,而jquery里边是一个类。这是不一样的地方。一些方法会挂在zepto对象上。比如Z()这个方法,一会会重点说。
接下来说的是zepto中的对象$.
$ = function(selector, context){ return zepto.init(selector, context) }
$是一个很重要的函数,接收两个参数,selector选择器,和context(可选的)。$也是定于在Zepto函数中,我们看他的代码实际上发现,调用$的时候,其实是调用了zepto对象上的init方法,那么这个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 = function(dom, selector) { return new Z(dom, selector) }
好吧,这个方法返回了一个Z对象,我们知道用new创建的对象需要构造函数,接下来自然我们要看一下构造函数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()这类的方法。
在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相关的东西。暂且不说了。