zepto源码研究 - zepto.js - 1
简要:网上已经有很多人已经将zepto的源码研究得很细致了,但我还是想写下zepto源码系列,将别人的东西和自己的想法写下来以加深印象也是自娱自乐,文章中可能有许多错误,望有人不吝指出,烦请赐教。
首先是整个大致流程:
Zepto 的整体结构
var Zepto = (function() { //实际构造函数`
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 = function(dom, selector) {
return new Z(dom, selector)
} //是否Zepto对象 instanceof 会循环查找左边的_prop_和右边的原型链中的原型是否匹配
zepto.isZ = function(object) {
return object instanceof zepto.Z
} // 初始化参数、DOM
zepto.init = function(selector, context) {
...
return zepto.Z(dom, selector)
} //构造函数
$ = function(selector, context){
return zepto.init(selector, context)
}
//原型设置
$.fn = { ... } zepto.Z.prototype = Z.prototype = $.fn $.zepto = zepto return $
})() // If `$` is not yet defined, point it to `Zepto`
window.Zepto = Zepto
window.$ === undefined && (window.$ = Zepto)
zepto开头就是变量的初始化: concat = emptyArray.concat, filter = emptyArray.filter, slice = emptyArray.slice,由于这些变量会经常用到,因此在这里缩短作用域链是优化的一种方法。
接下来是正则表达式:
//匹配HTML代码
fragmentRE = /^\s*<(\w+|!)[^>]*>/,
//TODO 匹配单个HTML标签
singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
//TODO 匹配自闭合标签
tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
//匹配根节点
rootNodeRE = /^(?:body|html)$/i,
//匹配A-Z
capitalRE = /([A-Z])/g,
zepto.matches :
/**
* 元素是否匹配选择器
* @param element
* @param selector
* @returns {*}
*/
zepto.matches = function(element, selector) {
//没参数,非元素,直接返回
if (!selector || !element || element.nodeType !== 1) return false //如果浏览器支持MatchesSelector 直接调用
var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector ||
element.oMatchesSelector || element.matchesSelector
if (matchesSelector) return matchesSelector.call(element, selector) //浏览器不支持MatchesSelector
var match, parent = element.parentNode, temp = !parent //元素没有父元素,存入到临时的div tempParent
if (temp) (parent = tempParent).appendChild(element) //再通过父元素来搜索此表达式。 找不到-1 找到有索引从0开始
//注意 ~取反位运算符 作用是将值取负数再减1 如-1变成0 0变成-1
match = ~zepto.qsa(parent, selector).indexOf(element) //清理临时父节点
temp && tempParent.removeChild(element) //返回匹配
return match
}
elem.matchesSelector(selector) 判断元素elem 是否匹配 selector,
例如:
$("#id").click(function (e) {
if(e.target.matches('a.btn')) {
e.preventDefault();
//TODO something
}
});
如果matchesSelector不支持,则找出elem的父元素(若没有则创造一个)利用zepto.qsa(parent, selector)来找出满足条件的元素
zepto.qsa(parent, selector):
/**
* 通过选择器表达式查找DOM
* 原理 判断下选择器的类型(id/class/标签/表达式)
* 使用对应方法getElementById getElementsByClassName getElementsByTagName querySelectorAll 查找
* @param element
* @param selector
* @returns {Array}
*/
zepto.qsa = function(element, selector){
var found,
maybeID = selector[0] == '#',//ID标识
maybeClass = !maybeID && selector[0] == '.',//class 标识
//是id/class,就取'#/.'后的字符串,如‘#test’取‘test'
nameOnly = maybeID || maybeClass ? selector.slice(1) : selector,
isSimple = simpleSelectorRE.test(nameOnly) //TODO:是否为单个选择器 没有空格
return (element.getElementById && isSimple && maybeID) ? // Safari DocumentFragment doesn't have getElementById
//通过getElementById查找DOM,找到返回[dom],找不到返回[]
( (found = element.getElementById(nameOnly)) ? [found] : [] ) :
//当element不为元素节点或document fragment时,返回空
//元素element 1 属性attr 2 文本text 3 注释comments 8 文档document 9 片段 fragment 11
(element.nodeType !== 1 && element.nodeType !== 9 && element.nodeType !== 11) ? [] :
slice.call(
//如果是class,通过getElementsByClassName查找DOM,
isSimple && !maybeID && element.getElementsByClassName ? // DocumentFragment doesn't have getElementsByClassName/TagName
maybeClass ? element.getElementsByClassName(nameOnly) : // If it's simple, it could be a class
element.getElementsByTagName(selector) : // Or a tag //如果是标签名,调用getElementsByTagName
//最后调用querySelectorAll
// Or it's not simple, and we need to query all
)
}
1:首先判断selector是ID类型还是class类型,将标识先赋值准备好 if(maybeID){
//如果是Id,则取Id
return found = element.getElementById(nameOnly)?[found]:[]
}
if(element.nodeType !== 1 && element.nodeType !== 9 && element.nodeType !== 11){
//如果不是正常的节点,则返回空
return [];
}else{
if(isSimple&&element.getElementsByClassName){
if(maybeClass){
return element.getElementsByClassName(nameOnly);
}else{
return element.getElementsByTagName(selector);
}
}else{
element.querySelectorAll(selector)
}
}
这里的nodeType 判断 1:是否为element元素,9:是否为document,11:是否为DocumentFragment
引用http://www.cnblogs.com/blueSkys/p/3685740.html中的一句话对DocumentFragment进行解释
DocumentFragment 节点不属于文档树,继承的 parentNode 属性总是 null。
不过它有一种特殊的行为,该行为使得它非常有用,即当请求把一个 DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 自身,而是它的所有子孙节点。这使得 DocumentFragment 成了有用的占位符,暂时存放那些一次插入文档的节点。它还有利于实现文档的剪切、复制和粘贴操作
element.querySelectorAll(selector)意思是 查找element的子元素并且document内css选择器符合selector的元素(这里并不像jquery.find一样查找
element的符合selector的子元素)
引用http://www.zhangxinxu.com/wordpress/2015/11/know-dom-queryselectorall/里面的内容:
<img id="outside">
<div id="my-id">
<img id="inside">
<div class="lonely"></div>
<div class="outer">
<div class="inner"></div>
</div>
</div>
document.querySelectorAll("#my-id div div").length === 1; ----> <div class="inner"></div>
document.querySelector("#my-id").querySelectorAll("div div").length === 3; ----> <div class="lonely"> <div class="outer"></div> <div class="inner"></div>
find:$(element).find(args); $(element)和args都可以是数组,这里主要介绍一下Array.filter和emptyArray.some,
/*
在当前集合中查找selector,selector可以是集合,选择器,以及节点
*/
find: function(selector){
var result, $this = this
//如果selector为node或者zepto集合时
if (!selector) result = $()
//遍历selector,筛选出父级为集合中记录的selector
else if (typeof selector == 'object')
result = $(selector).filter(function(){
var node = this
//如果$.contains(parent, node)返回true,则emptyArray.some也会返回true,外层的filter则会收录该条记录
//Array.some 如果有一个为true,则整体返回为true
return emptyArray.some.call($this, function(parent){
return $.contains(parent, node)
})
});
//如果selector是css选择器
//如果当前集合长度为1时,调用zepto.qsa,将结果转成zepto对象
else if (this.length == 1) result = $(zepto.qsa(this[0], selector))
//如果长度大于1,则调用map遍历
else result = this.map(function(){ return zepto.qsa(this, selector) })
return result
},
Array.filter(function(value){return true}) 将不符合条件的数组元素过滤掉,Array.some(function(){return true}) ,用来检查数组中是否
有满足条件的元素,如果有则返回true,find函数会先处理selector为对象和数组的情况,然后在处理为css选择器的情况。 本文主要介绍zepto的整体流程和初始化操作以及查询选择DOM的具体过程,下一篇具体介绍$()通过zepto.init使如何运作的
zepto源码研究 - zepto.js - 1的更多相关文章
- zepto源码研究 - deferred.js(jquery-deferred.js)
简要:zepto的deferred.js 并不遵守promise/A+ 规范,而在jquery v3.0.0中的defer在一定程度上实现了promise/A+ ,因此本文主要研究jquery v3. ...
- zepto源码研究 - fx_methods.js
简要:依赖fx.js,主要是针对show,hide,fadeIn,fadeOut的封装. 源码如下: // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zept ...
- zepto源码研究 - fx.js
简要:zepto 提供了一个基础方法animate来方便我们运用css动画.主要针对transform,animate以及普通属性(例如left,right,height,width等等)的trans ...
- zepto源码研究 - zepto.js (zepto.init)
简要:当我们用$()时,便会直接调用zepto.init 生成zepto对象,那zepto.init是如何根据不同类型的参数来生产指定对象呢? zepto.init = function(select ...
- zepto源码研究 - zepto.js - 6(模板方法)
width height 模板方法 读写width/height ['width', 'height'].forEach(function(dimension){ //将width,hegih ...
- zepto源码研究 - ajax.js($.ajax具体流程分析)
简要:$.ajax是zepto发送请求的核心方法,$.get,$.post,$.jsonp都是封装了$.ajax方法.$.ajax将jsonp与异步请求的代码格式统一起来,内部主要是先处理url,数据 ...
- zepto源码研究 - ajax.js($.ajaxJSONP 的分析)
简要:jsonp是一种服务器和客户端信息传递方式,一般是利用script元素赋值src来发起请求.一般凡是带有src属性的元素发起的请求都是可以跨域的. 那么jsonp是如何获取服务器的数据的呢? j ...
- zepto源码研究 - zepto.js - 5(dom属性管理)
index: $.fn = {...... indexOf: emptyArray.indexOf,} index: function(element){ //这里的$(element)[0]是为了将 ...
- zepto源码研究 - callback.js
简要:$.Callbacks是一个生成回调管家Callback的工厂,Callback提供一系列方法来管理一个回调列表($.Callbacks的一个私有变量list),包括添加回调函数, 删除回调函数 ...
随机推荐
- bestcoder单调区间
http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=584&pid=1006 题解:ORZ Matrix67 ht ...
- es6整理
1.const和let let类似于var,不同的是let只在所在的代码段有效 for循环的计数器,就很合适使用let命令. let和var的区别: //变量i是var声明的,在全局范围内都有效.所以 ...
- hdu 4407 Sum
http://acm.hdu.edu.cn/showproblem.php?pid=4407 题意:给定初始n个数1..n,两个操作,①1 x y p 询问第x个数到第y个数中与p互质的数的和; ② ...
- hdu 1051Wooden Sticks
#include<cstdio> #include<cstring> #include<algorithm> #define maxn 10000 using na ...
- GitHub上最火的开源项目SlidingMenu导入出错的终极解决方案
SlidingMenu 开源项目下载地址 : https://github.com/jfeinstein10/slidingmenu Actionbarsherlock 开源项目下载地址 :http: ...
- COJ 0990 WZJ的数据结构(负十)
WZJ的数据结构(负十) 难度级别:D: 运行时间限制:5000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 给你一个N个节点的有根树,从1到N编号,根节点为1并给 ...
- 使用 VMAccess 扩展程序重置 Linux 虚拟机的登录凭据
Ning KuangWSSC WS ARD高级项目经理 您是否曾经因为忘记 Azure VM 密码或 SSH密钥而导致无法访问 VM?VMAccess扩展程序使您可以重置密码.SSH密钥或 SSH ...
- 【动态规划】Codeforces 706C Hard problem
题目链接: http://codeforces.com/contest/706/problem/C 题目大意: n(2 ≤ n ≤ 100 000)个字符串(长度不超过100000),翻转费用为Ci( ...
- 错误 1 未能找到元数据文件“C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/Temporary ASP.NET Files/wwwroot/7cb4fcd
错误 1 未能找到元数据文件“C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/Temporary ASP.NET Files/wwwroot/7cb4fcd ...
- c宏的MAX函数
今天从香山上面回来累的跟傻逼一样,回来问了一下胡总的阿里面试的问题.然后其中有一个是宏写max函数.胡总说不好写,然后我就去洗澡了. 洗澡的时候感觉不对啊,回来写了一个: #define MAX(a, ...