manlili blog

Zepto源码解读3之典型函数

zepto.init 函数

先来看看zepto.init 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var Zepto = (function(){
var $,
zepto = {} //内部定义的zepto,非外部的Zepto
// ...省略N行代码...
zepto.init = function(selector, context) {
// 函数内容
}
$ = function(selector, context){
return zepto.init(selector, context)
}
// ...省略N行代码...
return $
})()
window.Zepto = Zepto
window.$ === undefined && (window.$ = Zepto)

从上面代码中我们可以知道让我们使用$()时候,首先定义一个zepto,然后在$里面将zepto.init的结果return出去,这时就要来讲解下zepto.init的具体内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and
// takes a CSS selector and an optional context (and handles various
// special cases).
// This method can be overridden in plugins.
zepto.init = function (selector, context) {
var dom
// 比如$(),这个时候需要返回给一个空的zepto对象
if (!selector) return zepto.Z()
// 如果这个参数是字符串,涉及到$('<div></div>'), $('p'), $("#id"), $(".class")...
else if (typeof selector === 'string') {
selector = selector.trim()
// If it's a html fragment, create nodes from it
// Note: In both Chrome 21 and Firefox 15, DOM error 12
// is thrown if the fragment doesn't begin with <
// 如果是以<开头的比如说是$('<div>'),$('<p>')这种标签形式的,就直接用document.createElement(这个标签)
if (selector[0] == '<' && fragmentRE.test(selector)) { dom = zepto.fragment(selector, RegExp.$1, context), selector = null }
// If there's a context, create a collection on that context first, and select
// nodes from there
// 如果是$(".chlidname", "#parentname")就根据parentname生成zepto对象,然后通过find寻找childname
else if (context !== undefined) return $(context).find(selector)
// If it's a CSS selector, use it to select nodes.
// 如果不是上面的两种情况,那么只剩下css选择器$(".name"), $("#name")这种,这个时候使用querySelectAll函数查找
else dom = zepto.qsa(document, selector)
}
// If a function is given, call it when the DOM is ready
// 如果$(function(){...}),那么就等DOM元素加载完成的时候运行这个函数
else if (isFunction(selector)) return $(document).ready(selector)
// If a Zepto collection is given, just return it
// 如果selector本身就是一个zepto对象,直接return出去
else if (zepto.isZ(selector)) return selector
// 当selector即不是空对象,也不是字符串、函数或者zepto对象,那么
else {
// normalize array if an array of nodes is given
// 如果selector是个数组,那么将数组祛除空的项以后赋值给dom
if (isArray(selector)) dom = compact(selector)
// Wrap DOM nodes.
// 如果数组是对象,那么将对象转为数组赋值给dom
else if (isObject(selector)) { dom = [selector], selector = null }
// If it's a html fragment, create nodes from it
else if (fragmentRE.test(selector)) { dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null }
// If there's a context, create a collection on that context first, and select
// nodes from there
else if (context !== undefined) return $(context).find(selector)
// And last but no least, if it's a CSS selector, use it to select nodes.
else dom = zepto.qsa(document, selector)
}
// create a new Zepto collection from the nodes found
// 注意,dom是数组
return zepto.Z(dom, selector)
}

上面的总体来说呢就是

1
2
3
4
5
6
7
8
9
10
11
12
zepto.init = function(selector, context) {
var dom
// 分情况对dom赋值:
// 1. selector 为空
// 2. selector 是字符串,其中又分好几种情况
// 3. selector 是函数
// 4. 其他情况,例如 selector 是数组、对象等
// 最后去调用zepto.Z
return zepto.Z(dom, selector)
}

zepto.Z 函数

下面来看下zepto.Z的最初的一版源码,主要是比较好理解

1
2
3
4
5
6
7
8
9
10
11
// `$.zepto.Z` swaps out the prototype of the given `dom` array
// of nodes with `$.fn` and thus supplying all the Zepto functions
// to the array. Note that `__proto__` is not supported on Internet
// Explorer. This method can be overriden in plugins.
zepto.Z = function(dom, selector) {
//dom是个数组,将dom数组的隐式原型与$.fn连在一起,$.fn就是一个看似很吓人,其实就是一个普通的对象罢了
dom = dom || []
dom.__proto__ = $.fn
dom.selector = selector || ''
return dom
}

好了,为了看懂$.fn,我们去找下$.fn的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$.fn = {
// Because a collection acts like an array
// copy over these useful array functions.
forEach: emptyArray.forEach,
reduce: emptyArray.reduce,
push: emptyArray.push,
sort: emptyArray.sort,
splice: emptyArray.splice,
indexOf: emptyArray.indexOf,
concat: function(){
var i, value, args = []
for (i = 0; i < arguments.length; i++) {
value = arguments[i]
args[i] = zepto.isZ(value) ? value.toArray() : value
}
return concat.apply(zepto.isZ(this) ? this.toArray() : this, args)
},
// `map` and `slice` in the jQuery API work differently
// from their array counterparts
map: function(fn){
return $($.map(this, function(el, i){ return fn.call(el, i, el) }))
},
slice: function(){
return $(slice.apply(this, arguments))
}
}

上面只截取了一部分,但是可以显而易见的发现$.fn就是一个很普通的对象,上面挂着一堆键值对,其实就是我们介绍的原型

但是后来zepto改版了,升级了zepto.Z的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//将数组写成对象形式{0:xx, 1:xx, 2:xx}
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 || ''
}
// `$.zepto.Z` swaps out the prototype of the given `dom` array
// of nodes with `$.fn` and thus supplying all the Zepto functions
// to the array. This method can be overridden in plugins.
zepto.Z = function(dom, selector) {
return new Z(dom, selector)
}
$.fn = {
// ...很多属性...
}
//注意这里,原型的继承
zepto.Z.prototype = Z.prototype = $.fn

请我喝杯果汁吧!