咱们移动端基本使用zepto了,而我也从一个小白变成稍微靠谱一点的前端了,最近竟然常常要改到zepto源码
可是,我对zepto不太熟悉,其实前端水准仍是不够,因此便私下偷偷学习下吧,别被发现了javascript
咱们使用这个方法通常有几个用途(我这里只说我本身用到过的),这里根据使用度排个序:css
将返回一个包装过的dom集合对象(有不少选择器)html
仍然返回一个包装过的dom对象,他会将字符串初始化为咱们的dom结构
PS:新增了一个方法能够直接赋予dom结构属性,咱们这里不关注前端
我基本没这么用过,他实在文档加载结束后执行java
暂时无论这些东西吧。咱们这里来一个个看看他们的实现:node
1 $('div') 2 //=> all DIV elements on the page 3 $('#foo') 4 //=> element with ID "foo" 5 6 // create element: 7 $("<p>Hello</p>") 8 //=> the new P element 9 // create element with attributes: 10 $("<p />", 11 { 12 text: "Hello", 13 id: "greeting", 14 css: { color: 'darkblue' } 15 }) 16 //=> <p id=greeting style="color:darkblue">Hello</p> 17 // execute callback when the page is ready: 18 Zepto(function ($) { 19 alert('Ready to Zepto!') 20 })
首先咱们来看看选择器,在zepto代码中有这么一行代码:数组
1 $ = function (selector, context) { 2 return zepto.init(selector, context) 3 }
这里其实前面就定义了$这个变量:dom
var undefined, key, $, classList, emptyArray = [],
这里初始化了$这个变量为一个函数,最后并将这个变量公开化(如今再匿名仍是中,外面是访问不到的),他具体是这样干的函数
1 var Zepto = function () { 2 var zepto = {}, $; 3 zepto.init = function (selector, context) { 4 }; 5 $ = function (selector, context) { 6 return zepto.init(selector, context); 7 }; 8 $.zepto = zepto; 9 return $; 10 }; 11 window.$ = Zepto;
因而咱们在页面中使用这样的代码:学习
1 var el = $('#id'); 2 //其实是 3 var el = zepto.init('#id');
具体里面的逻辑就是咱们的重点,因而咱们跟进去慢慢看吧
咱们详细看看zepto.init这个方法
1 zepto.init = function (selector, context) { 2 if (!selector) return zepto.Z() 3 else if (isFunction(selector)) return $(document).ready(selector) 4 else if (zepto.isZ(selector)) return selector 5 else { 6 var dom 7 if (isArray(selector)) dom = compact(selector) 8 else if (isObject(selector)) 9 dom = [isPlainObject(selector) ? $.extend({}, selector) : selector], selector = null 10 else if (fragmentRE.test(selector)) 11 dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null 12 else if (context !== undefined) return $(context).find(selector) 13 else dom = zepto.qsa(document, selector) 14 return zepto.Z(dom, selector) 15 } 16 }
第一步,若是选择器selector不存在的话,就调用默认的方法,说白了就是返回一个集合长度为0的包装对象
1 //经过给dom设置__proto__属性指向$.fn来达到继承$.fn上全部方法的目的 2 //ie天然是不支持的,zepto也基本不理睬ie 3 zepto.Z = function (dom, selector) { 4 dom = dom || [] 5 dom.__proto__ = $.fn 6 dom.selector = selector || '' 7 return dom 8 }
第二步,处理传入函数状况,若是传入是函数的话就在文档加载结束后执行
第三步,处理传入zepto对象,若是已是zepto包装对象了就直接返回
1 zepto.isZ = function (object) { 2 return object instanceof zepto.Z 3 }
第四步,开始了复杂的逻辑计算了,咱们这里单独提出来
若是对象是一个数组,则去掉其中无心义的数组项
1 //清除给定的参数中的null或undefined,注意0==null,'' == null为false 2 function compact(array) { 3 return filter.call(array, function (item) { 4 return item != null 5 }) 6 }
其中filter是javascript数组的一个新的方法,用以筛选知足条件的数组项
1 var arr = [5, "element", 10, "the", true]; 2 var result = arr.filter( 3 function (value) { 4 return (typeof value === 'string'); 5 } 6 ); 7 document.write(result);//Output: element, the
最后返回数组项,咱们这里认为其中每一个数组项都是一个dom结构
由于typeof dom也是object,因此zepto这里作了一点扩展
1 function isObject(obj) { 2 return type(obj) == "object" 3 }
这个代码存在的意义,老夫也不知道了......if里面的代码比较关键:
若是传入的是对象(好比{}),就将selector拷贝到一个对象,若是是dom结构就直接放入数组
这里有两个方法须要注意,一个是extend,一个是isPlainObject
extend用于为对象扩展对象
1 function extend(target, source, deep) { 2 for (key in source) 3 //若是深度扩展 4 if (deep && (isPlainObject(source[key]) || isArray(source[key]))) { 5 //若是要扩展的数据是对象且target相对应的key不是对象 6 if (isPlainObject(source[key]) && !isPlainObject(target[key])) target[key] = {} 7 //若是要扩展的数据是数组且target相对应的key不是数组 8 if (isArray(source[key]) && !isArray(target[key])) target[key] = [] 9 extend(target[key], source[key], deep) 10 } else if (source[key] !== undefined) target[key] = source[key] 11 }
$.extend事实上也是调用的上面的方法,咱们这里先无论他
1 $.extend = function (target) { 2 var deep, args = slice.call(arguments, 1) 3 if (typeof target == 'boolean') { //当第一个参数为boolean类型的值时,表示是否深度扩展 4 deep = target 5 target = args.shift() //target取第二个参数 6 } 7 //遍历后面的参数,所有扩展到target上 8 args.forEach(function (arg) { 9 extend(target, arg, deep) 10 }) 11 return target 12 }
这个方法有所不一样,经过字面量定义的对象和new Object的对象返回true,new Object时传参数的返回false(测试对象是否纯粹的对象)
1 function isPlainObject(obj) { 2 return isObject(obj) && !isWindow(obj) && obj.__proto__ == Object.prototype 3 } 4 $.isPlainObject({})// => true 5 $.isPlainObject(new Object) // => true 6 $.isPlainObject(new Date) // => false 7 $.isPlainObject(window) // => false
至于这个数组究竟是不是dom,代码这里先不关注,完了这里也结束了
若是传入的是html字符串,咱们这里就要负责建立dom的工做了,整个这个东西其实比较复杂:
1 //HTML代码片段的正则 2 fragmentRE = /^\s*<(\w+|!)[^>]*>/,
PS:我正则不行,暂时就不尝试去解释这个了
字符串=>dom
1 zepto.fragment = function (html, name, properties) { 2 //将相似<div class="test"/>替换成<div class="test"></div>,算是一种修复吧 3 if (html.replace) html = html.replace(tagExpanderRE, "<$1></$2>") 4 //给name取标签名 5 if (name === undefined) name = fragmentRE.test(html) && RegExp.$1 6 //设置容器标签名,若是不是tr,tbody,thead,tfoot,td,th,则容器标签名为div 7 if (!(name in containers)) name = '*' 8 var nodes, dom, container = containers[name] //建立容器 9 container.innerHTML = '' + html //将html代码片段放入容器 10 //取容器的子节点,这样就直接把字符串转成DOM节点了 11 dom = $.each(slice.call(container.childNodes), function () { 12 container.removeChild(this) //逐个删除 13 }) 14 //若是properties是对象, 则将其看成属性来给添加进来的节点进行设置 15 if (isPlainObject(properties)) { 16 nodes = $(dom) //将dom转成zepto对象,为了方便下面调用zepto上的方法 17 //遍历对象,设置属性 18 $.each(properties, function (key, value) { 19 //若是设置的是'val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset',则调用zepto上相对应的方法 20 if (methodAttributes.indexOf(key) > -1) nodes[key](value) 21 else nodes.attr(key, value) 22 }) 23 } 24 //返回将字符串转成的DOM节点后的数组,好比'<li></li><li></li><li></li>'转成[li,li,li] 25 return dom 26 }
这个方法比较高深,各位慢慢消化,我稍后也再去消化下
以上完了后,就返回了建立后的dom结构了......
若是带有上下文,就得使用上下文查找,没有就在document中查询
1 zepto.qsa = function (element, selector) { 2 var found 3 return (isDocument(element) && idSelectorRE.test(selector)) ? 4 ((found = element.getElementById(RegExp.$1)) ? [found] : []) : 5 (element.nodeType !== 1 && element.nodeType !== 9) ? [] : 6 slice.call( 7 classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1) : 8 tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) : 9 element.querySelectorAll(selector) 10 ) 11 }
这个函数与上面函数同样重要,各位下去消化吧
最后的最后便返回咱们的包装集合了,这里依旧使用zepto.Z(dom, selector)进行封装
待续......
待续......
今天的学习暂时到这里,咱们下次继续。