manlili blog

zepto源码解读2之架构设计

源码地址https://github.com/manlili/zepto-source-analysis里面的introduction.html

Zepto原理说明

先来看一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script src="zepto.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<p id="p1">测试</p>
<div>
<span>test</span>
<span>test</span>
<span>test</span>
</div>
</body>
</html>

这个时候再打开console控制台输入

1
2
3
4
5
6
7
8
9
10
11
12
var $p = $('p'); // $p 是数组
var $span = $('span'); // $span 是数组
var arr = [1,2,3];
var $p = $('p');
// 对比1
arr.__proto__.constructor === Array; // true
$p.__proto__.constructor === Array; // false
// 对比2
arr instanceof Array; // true
$p instanceof Array; // false

这个时候发现上面两个输出的都是数组,下面再来一段代码

1
$("span").addClass('test')

会发现浏览器html中所有P标签都加了类名test,但是数组没有内置的addClass方法,这就是我们《zepto源码解读1之引言》提到的扩展隐式原型来实现的。

如果此时我想创建一个新的对象,他调用原生数组的内置对象concat和push,同时又自定义addClass方法,只需要

1
2
3
4
5
6
7
8
var attr = [1,2,3]
attr.__proto__ = {
concat: Array.prototype.concat,
push: Array.prototype.push,
addClass: function () {
console.log("this is addClass")
}
}

这个时候再打开console控制台输入

1
2
3
4
5
attr.push(4)
attr
var test = attr.concat([5,6,7,8])
console(test)
attr.addClass()

经测试上面的代码都是正确的。

实际上zepto.js设计的时候,并不像上面的那么简单,但是原理都是一致的

紧接着上面的attr创建新方法addClass,这里需要注意的是,实际上addClass方法只在attr上面起作用,假设我们再创建一个

1
var attr2 = [11,12,13]

这个时候的attr2依然没有addClass方法,如果想让attr2也有的话,就需要用

1
2
3
attr.__proto__.constructor.prototype.addClass = function () {
console.log("this is addClass")
}

上面attr.__proto__.constructor是Array,也就是直接扩展了Array,这也是我们扩展插件的主要思想

Zepto架构设计

下来看一段代码

1
2
3
4
5
var Zepto = (function () {
})() //使用函数表达式风格的自执行函数
window.Zepto = Zepto
window.$ === undefined && (window.$ = Zepto)

关于自执行函数建议看我的另外一篇博客立即执行函数IIFE
上面代码最难理解的是

1
window.$ === undefined && (window.$ = Zepto)

解释起来就是如果window.$还没有被定义,就将Zepto赋值给window

细心的人就会发现,立即执行函数里面没有return,下面来看下稍微完整点的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var Zepto = (function(){
var $
// ...省略N行代码...
$ = function(selector, context){
return zepto.init(selector, context)
}
// ...省略N行代码...
return $
})()
window.Zepto = Zepto
window.$ === undefined && (window.$ = Zepto)

这个时候再打开console控制台输入和结果是

1
2
3
4
5
6
7
8
9
10
11
12
window.Zepto
ƒ (selector, context){
return zepto.init(selector, context)
}
window.$
ƒ (selector, context){
return zepto.init(selector, context)
}
typeof window.$
"function"

由上面的代码可以看出window.$也就是Zepto最终是个函数

请我喝杯果汁吧!