Zepto源码分析之一(代码结构及初始化)

关于读源码,读jQuery自然是不错,但太过于庞大不易解读,对于小白,最好从Zepto,Lodash这样的小库入手。

这里使用的是zepto1.1.6版本为例。

 

自执行函数

在阅读之前,先弄清楚闭包和自执行函数

两种方式: (function() {})() 和 (function() {}())

1 (function() {
2   console.log('这里直接执行')
3 })()
1 (function () {
2   console.log('这里直接执行')
3 }())

自执行函数的优势在于,避免了全局变量的污染,即在自执行函数体内声明的变量,外部是无法访问到的。

1 (function () {
2     let val = '123'
3 })()
4 
5 console.log(val)   // val is not defined

如需要获取变量val的值,只能在函数体内部返回,然后将函数赋值给一个全局变量

1 let temp= (function () {
2   let val = '123';
3   return val;     // 必须return, 否则获取不到值
4 })()
5 
6 window.temp = temp;
7 
8 console.log(temp)   // 123

而关于闭包,简单理解就是在自执行函数内部声明变量或方法,在自执行函数内就形成了一个闭合的上下文环境,外部是无法直接访问的。

 

源码结构

1 ;let Zepto = (function () {
2     let $, zepto = {};
3 
4    return $;   // 最终返回值
5 })()
6 
7 window.Zepto = Zepto;
8 window.$ === undefined && (window.$ = Zepto);

首先声明一个字支持函数并赋给变量Zepto, 再将变量Zepto赋给全局变量window.Zepto

如果$未被声明,则也将Zepto赋给window.$全局变量

此时,外部访问Zepto或者$都可以返回自执行函数内部的$(即 return $)

 

再看$到底是什么

 $返回一个函数,传入的参数是selector选择器(即tagName,id,或className),context暂为空。

 1 ;let Zepto = (function () {
 2   let $, zepto = {};
 3   $ = function(selector, context) {
 4     console.log('获取节点');
 5     return zepto.init(selector, context);
 6   };
 7   return $;
 8 })();
 9 
10 window.Zepto = Zepto;
11 window.$ === undefined && (window.$ = Zepto);

此时,如果访问节点, 会输出'获取节点'

1 <div>
2     <span>测试</span>
3     <span>测试</span>
4     <span>测试</span>
5 </div>
1 let span = $('span');
2 console.log(span)

这里报错是因为init方法还未定义;

继续创建 init方法

init函数是绑定在zepto对象上的方法(之前已经声明一个zepto空对象)

1 zepto.init = function(selector, context) {
2     let dom;
// 处理dom的代码
3 return zepto.Z(dom, selector); 4 };

init处理dom部分,分为几种情况

1 . 当不传参数 $(), 直接返回zepto.Z()不传参

1 if (!selector) return zepto.Z()

2 . 当传入字符串参数,又分为几种

 1 else if (typeof selector == 'string') {
 2       selector = selector.trim()
 3 
 4       if (selector[0] == '<' && fragmentRE.test(selector))
 5         dom = zepto.fragment(selector, RegExp.$1, context), selector = null
 6      
 7       else if (context !== undefined) return $(context).find(selector)
 8     
 9       else dom = zepto.qsa(document, selector)   //这里的qsa其实就是document.querySelectorAll()
10 }

  (1) $(' span ')

  首先将清除字符串前后空格 selector.trim()

  (2) $('<div></div>')

  如果第一个字符是<,并且正则fragmentRE成立,

  则使用fragment方法处理dom节点

1 if (selector[0] == '<' && fragmentRE.test(selector))
2         dom = zepto.fragment(selector, RegExp.$1, context), selector = null

  

  (3) $('span', 'div')

  如果传入第二个参数,即之前的context不等于undefined,则相当于执行$('div').find('span')

1 else if (context !== undefined) return $(context).find(selector)

  若div中存在span则返回span,否则返回空数组。

       (4)如以上三种不满足则执行最后一步,zepto.qsa()

 

 

 3 . 如果传入函数,用document调用一个zepto对象,然后$.ready()方法, ready方法和上面的find方法一样在后面创建。

1 else if (isFunction(selector)) return $(document).ready(selector);

 

4 . 若已经是一个zepto对象,则直接返回该对象,不必再调用$(), 即 $($('span')) === $('span')

1 else if (zepto.isZ(selector)) return selector
1 zepto.isZ = function(object) {
2     return object instanceof zepto.Z
3 }

 

5 . 若是引用类型

 1 else {
 2       // 如参数是数组,则调用compact方法, 会将null或undefined剔除
 3       if (isArray(selector)) dom = compact(selector)
 4       // 如是DOM节点,则数组化
 5       else if (isObject(selector))
 6         dom = [selector], selector = null
 7       // 以下和上面字符串参数的重复,何意未知。
 8       else if (fragmentRE.test(selector))
 9         dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
10       // 
11       // 
12       else if (context !== undefined) return $(context).find(selector)
13       // 
14       else dom = zepto.qsa(document, selector)
15     }

 

 

init是一个初始化方法,调用init时去执行zepto.Z()函数

1 zepto.Z = function(dom, selector) {
2     dom = dom || []
3     dom.__proto__ = $.fn;
4     dom.selector = selector || '';
5     console.log(dom)
6     console.log(selector)
7     return dom;
8  };

 

关于Z()

1 . 传入dom,如果未传则返回[]空数组

  如果不传DOM节点 $(), 则返回一个空值。

  

2 . 如果有dom $('span'),则

       

3 . 同时在该数组的原型对象上创建$.fn对象

  在未定义$.fn之前,dom.__proto__原型对象上的方法是Array构造函数的内置方法

  

  而这里使用了dom.__proto__ = $.fn 重新定义了原型对象上的方法

1 $.fn = {
2     get: function () {
3       console.log('自定义get方法')
4     },
5     on: function () {
6       console.log('自定义on方法')
7     }
8 }

  

可以看出,通过原型对象上创建新方法后,原来的内置方法都没有了。

如果只是想追加方法,则应该在原型对象上添加属性。

dom.__proto__.get = function() {}

dom.__proto__.on = function() {}

  

4 . 最后返回该DOM节点对象。此时$('span')对象就可以调用方法get和on了。

 

转载于:https://www.cnblogs.com/hughes5135/p/10494960.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值