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