zepto源码解读(二)——zpeto.init()函数——(1)结构整理
前面第一节我们大致阐述了zepto的设计结构,看起来东西很多,但是化整为零就没那么繁杂了,一起来看一看,首先我们来看一看zepto源码中可以说是最关键的函数之一zepto.init()。
我们之前说到了$这个api会导致函数zepto返回$这个接口,$呢,是一个函数:
1 // `$` will be the base `Zepto` object. When calling this 2 // function just call `$.zepto.init, which makes the implementation 3 // details of selecting nodes and creating Zepto collections 4 // patchable in plugins. 5 $ = function(selector, context){ 6 return zepto.init(selector, context) 7 }
可以看到$函数会返回另一个名为zepto.init()的函数,并且将传入的参数响应传递进去。(这里之所以直接给$赋值而不是var $因为之前已经声明了变量$,所以变量$并不会污染全局)。
那么我们接着来看一看zepto.init()是一个什么样的函数,从字面上看init是初始化的意思。代码如下:
1 zepto.init = function(selector, context) { 2 var dom 3 // If nothing given, return an empty Zepto collection 4 if (!selector) return zepto.Z() 5 // Optimize for string selectors 6 else if (typeof selector == 'string') { 7 selector = selector.trim() 8 // If it's a html fragment, create nodes from it 9 // Note: In both Chrome 21 and Firefox 15, DOM error 12 10 // is thrown if the fragment doesn't begin with < 11 //如果是<开头 >结尾 基本的HTML代码时 12 if (selector[0] == '<' && fragmentRE.test(selector)) 13 //调用片段生成dom 14 dom = zepto.fragment(selector, RegExp.$1, context), selector = null 15 // If there's a context, create a collection on that context first, and select 16 // nodes from there 17 else if (context !== undefined) return $(context).find(selector) 18 // If it's a CSS selector, use it to select nodes. 19 //通过css表达式查找元素 20 else dom = zepto.qsa(document, selector) 21 } 22 // If a function is given, call it when the DOM is ready 23 else if (isFunction(selector)) return $(document).ready(selector) 24 // If a Zepto collection is given, just return it 25 else if (zepto.isZ(selector)) return selector 26 else { 27 // normalize array if an array of nodes is given 28 if (isArray(selector)) dom = compact(selector) 29 // Wrap DOM nodes. 30 else if (isObject(selector)) 31 dom = [selector], selector = null 32 // If it's a html fragment, create nodes from it 33 else if (fragmentRE.test(selector)) 34 dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null 35 // If there's a context, create a collection on that context first, and select 36 // nodes from there 37 else if (context !== undefined) return $(context).find(selector) 38 // And last but no least, if it's a CSS selector, use it to select nodes. 39 else dom = zepto.qsa(document, selector) 40 } 41 // create a new Zepto collection from the nodes found 42 return zepto.Z(dom, selector) 43 }
看起来很复杂对不对,还是用化整为零的思路,我们一步一步分开来看。首先看一下它的结构,用了很多的if else来判断并且处理传入的参数。最终!return zepto.Z()这个函数,可想而知,这个init函数应该是处理参数并且传出两个参数给zepto.Z(),一个名为dom,一个名为selector。至于这个zepto.Z是什么我们先不管,我们来好好看看其中几十行的if...else..是做什么用的。
· 无参数,即$()
1 if (!selector) return zepto.Z()
· selector
参数是字符串,例如$('p')
$('<div>')
$('#content')
1 else if (typeof selector == 'string') { 2 selector = selector.trim() 3 // If it's a html fragment, create nodes from it 4 // Note: In both Chrome 21 and Firefox 15, DOM error 12 5 // is thrown if the fragment doesn't begin with < 6 //如果是<开头 >结尾 基本的HTML代码时 7 if (selector[0] == '<' && fragmentRE.test(selector)) 8 //调用片段生成dom 9 dom = zepto.fragment(selector, RegExp.$1, context), selector = null 10 // If there's a context, create a collection on that context first, and select 11 // nodes from there 12 else if (context !== undefined) return $(context).find(selector) 13 // If it's a CSS selector, use it to select nodes. 14 //通过css表达式查找元素 15 else dom = zepto.qsa(document, selector) 16 }
首先用trim()函数处理selecor参数,清除参数中的空格。
情况1:参数为<div>
这种形式,即是一个html标签的,那么dom
变量会被赋值为用这个标签创建的DOM对象,就像dom = document.createElement('div')
差不多。其中涉及到了fragmentRE
和zepto.fragment
两个我们尚未了解的东东,此处不要深究,知道这段代码的意思即可。
情况2,如果第二个参数有值,则先根据第二个参数生成zepto对象,(此处算是一个回调函数)然后再调用.find
来获取,例如$('.item', '#content')
这种用法。find()
方法是zepto对象的一个函数。
情况3,以上两种情况都不是,则调用zepto.qsa
来获取数据,后来聊这个方法的具体实现。qsa
即querySelectAll
的缩写。
· selector
参数是函数,例如$(function(){...})
1 else if (isFunction(selector)) return $(document).ready(selector)
用到了isFunction()来判断是否为函数以及ready()函数的使用
· selector
本身就是个zepto对象
这种用法比较少,但是也不能避免,例如:
1 var a = $('p'); 2 $(a); // 这里传入的 a 本身就是个 zepto 对象了。
· 其他情况
1 else { 2 // normalize array if an array of nodes is given 3 if (isArray(selector)) dom = compact(selector) 4 // Wrap DOM nodes. 5 else if (isObject(selector)) 6 dom = [selector], selector = null 7 // If it's a html fragment, create nodes from it 8 else if (fragmentRE.test(selector)) 9 dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null 10 // If there's a context, create a collection on that context first, and select 11 // nodes from there 12 else if (context !== undefined) return $(context).find(selector) 13 // And last but no least, if it's a CSS selector, use it to select nodes. 14 else dom = zepto.qsa(document, selector) 15 }
情况1:selector
参数是数组,则通过一个compact()
处理一下赋值给dom
。
if (isArray(selector)) dom = compact(selector)
情况2:selector
参数是DOM节点,则将它作为数组赋值给dom。
1 else if (isObject(selector)) 2 dom = [selector], selector = null
剩余情况:类似于字符串的操作,只是在输入时没有加入'' "导致不是字符串的情况。
1 else if (fragmentRE.test(selector)) 2 dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null 3 // If there's a context, create a collection on that context first, and select 4 // nodes from there 5 else if (context !== undefined) return $(context).find(selector) 6 // And last but no least, if it's a CSS selector, use it to select nodes. 7 else dom = zepto.qsa(document, selector)
总结
zepto.init
函数算是zepto源码中比较复杂的一个函数,一开篇就遇到了个硬骨头。不过我们这里暂且先把那些分叉放在一边,先把大路疏通,然后在慢慢的去一个一个攻破那些分叉。
接下来我们再把init
函数的结构梳理一下。
1 zepto.init = function(selector, context) { 2 var dom 3 4 // 分情况对dom赋值: 5 // 1. selector 为空 6 // 2. selector 是字符串,其中又分好几种情况 7 // 3. selector 是函数 8 // 4. 其他情况,例如 selector 是数组、对象等 9 10 // create a new Zepto collection from the nodes found 11 return zepto.Z(dom, selector) 12 }