zepto模块-zepto源码

前言

zepto是一个轻量级的类jQuery的JavaScript库,适合在移动端页面开发使用。轻量又提供许多dom操作API。

  • 下面是zepto文档的zepto整体模块的介绍
module description
zepto 核心模块;包含许多方法
event 通过on()& off()处理事件
ajax XMLHttpRequest 和 JSONP 实用功能
form 序列化 & 提交web表单
ie 增加支持桌面的Internet Explorer 10+和Windows Phone 8。
detect 提供 $.os和 $.browser消息
fx The animate()方法
fx_methods 以动画形式的 show, hide, toggle, 和 fade*()方法.
assets 实验性支持从DOM中移除image元素后清理iOS的内存。
data 一个全面的 data()方法, 能够在内存中存储任意对象。
deferred 提供 $.Deferredpromises API. 依赖”callbacks” 模块. 当包含这个模块时候, $.ajax() 支持promise接口链式的回调。
callbacks 为”deferred”模块提供 $.Callbacks。
selector 实验性的支持 jQuery CSS 表达式 实用功能,比如 $(‘div:first’)和 el.is(‘:visible’)。
touch 在触摸设备上触发tap– 和 swipe– 相关事件。这适用于所有的touch(iOS, Android)和pointer事件(Windows Phone)。
gesture 在触摸设备上触发 pinch 手势事件。
stack 提供 andSelf& end()链式调用方法
ios3 String.prototype.trim 和 Array.prototype.reduce 方法 (如果他们不存在) ,以兼容 iOS 3.x.

zepto模块

我们先来看一下zepto模块的源码都写了什么
image

可以看出,zepto这个模块主要实现了$()的实现,以及为其提供$.fn对象上若干方法,用于操作。

$()方法

假使我们在引入zepto的js文件执行下面代码,zepto会做些什么?

1
2
3
4
5
$()
$("<div />", { text:"Hello", id:"greeting", css:{color:'darkblue'} })
$("<div /><p />", { text:"Hello", id:"greeting", css:{color:'darkblue'} })
$(["<a>",null,"sd"])
$("<p>Hello</p>")

让我们看一下zepto模块的$()代码到底做了什么?

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// 定义$ 方法
$ = function(selector, context){
return zepto.init(selector, context)
}

// 执行zepto.init
zepto.init = function(selector, context) {

if(!selector){
// 如果不传参数的话
return zepto.Z(); // 待会再讲,这里会返回一个原型为$.fn的对象,可以调用fn上的方法。
}


else if(typeof selector == 'string'){
//如果"selector"传入的是字符串的话

if (selector[0] == '<' && fragmentRE.test(selector)){
// 如果为 "<>"这种创建节点
zepto.fragment(); //创建节点
}
else if(context !== undefined){
// 不包含属性内容
return $(context).find(selector) // 查找节点
}
else {
// 传入的是一个css选择器
zepto.qsa(document, selector)
}

}

else if(isFunction(selector)){
// 如果传入的是一个函数的话
$(document).ready(selector) // 调用ready
}

else{
if (isArray(selector)){
// 传入的是数组
dom = compact(selector) // 消除null元素,返回一个数组对象
}
else if (isObject(selector)){
dom = [selector], selector = null
}
else if (fragmentRE.test(selector)){
dom = zepto.fragment()
}
else if (context !== undefined) return $(context).find(selector)
else dom = zepto.qsa(document, selector)
}
return zepto.Z(dom, selector)
// 创建一个构造函数Z的实例对象,原型为$.fn
}

// 执行zepto.Z()
zepto.Z = function(dom, selector) {
return new Z(dom, selector)
}

// 构造函数Z
function Z(dom, selector) {
var i, len = dom ? dom.length : 0
for (i = 0; i < len; i++) this[i] = dom[i]

// console.log(this)

this.length = len
this.selector = selector || ''
}
// 将Z的原型执行$.fn,使其可以使用上面的若干方法。
zepto.Z.prototype = Z.prototype = $.fn

这里执行到最后,返回一个Z实例对象,包含了dom集合,原型继承$.fn对象。

$.fn对象

Zepto.fn是一个对象,它拥有Zepto对象上所有可用的方法,如 addClass(), attr(),和其它方法。

我们不讲解其中方法的具体实现,有兴趣的同学可以看下面给出的源码。

$.extend()

$.extend(target, [source, [source2, …]])
$.extend(true, target, [source, …])

  • 通过源对象扩展目标对象的属性,源对象属性将覆盖目标对象属性。
  • 默认情况下为,复制为浅拷贝(浅复制)。如果第一个参数为true表示深度拷贝(深度复制)。
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

function extend(target, source, deep) {
for (key in source)
if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
// 进行深拷贝
if (isPlainObject(source[key]) && !isPlainObject(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 = function(target){
var deep, args = slice.call(arguments, 1)
// 如果传入的第一个参数为布尔值,指明深度拷贝
if (typeof target == 'boolean') {
deep = target
target = args.shift()
}
args.forEach(function(arg){ extend(target, arg, deep) })
return target
}

下面思考一下

1
2
3
4
5
6
7
8
9
10
11
var target = { one: 'patridge' },
source = { two: ['turtle doves'] }
var target2 = { one: 'patridge2' },
source2 = { two: ['turtle doves2'] }
$.extend(true , target, source)
$.extend(target2, source2)
target.two.push('a');
target2.two.push('a');

console.log(source.two)
console.log(source2.two)

chrome浏览器控制台输出:
console.log(source.two) // [“turtle doves”]
console.log(source2.two) // [“turtle doves2”,”a”]

如果对上面结果有疑问的话,请重新理解“浅复制”和“深拷贝”的概念,并了解”JavaScript 值的引用”的知识。

$.contain()

$.contains(parent, node) ⇒ boolean

  • 检查父节点是否包含给定的dom节点,如果两者是相同的节点,则返回 false。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

$.contains = document.documentElement.contains ?
function(parent, node) {
return parent !== node && parent.contains(node)
} :
function(parent, node) {
while (node && (node = node.parentNode))
if (node === parent) return true
return false
}

// js原生方法说明

* Node.contains()返回一个布尔值来表示是否传入的节点是,该节点的子节点。
* IE9及以上可以使用
* eg:
function isInPage(node) {
return (node === document.body) ? false : document.body.contains(node);
}

其他说明

下面是由于自身知识缺陷,在阅读源码的时候重新翻阅资料理解的。读者可以选择阅读,或者直接跳过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// trim

$.trim = function(str) {
return str == null ? "" : String.prototype.trim.call(str)
}
// 兼容旧环境,该方法只支持IE9以上,下面这段代码是mdn上的。
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
};
}


// RegExp.$1-$9

* 非标准$1, $2, $3, $4, $5, $6, $7, $8, $9 是正则表达式括号字符串中的静态只读匹配项。
* 属性的值是只读的,只有在正确匹配的情况下才会 改变
* eg:
var re = /(\w+)\s(\w+)/;
var str = 'John Smith';
str.replace(re, '$2, $1'); // "Smith, John"
RegExp.$1; // "John"
RegExp.$2; // "Smith"

附录:

zepto模块源码(注:此为拷贝版本,对应的是本博文的内容,最新版本请到zepto官网获取。)

示例代码:zepto.html