关于读源码,读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) //

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

源码结构

  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了。

Zepto源码分析之一(代码结构及初始化)的更多相关文章

  1. Vue.js 源码分析(一) 代码结构

    关于Vue vue是一个兴起的前端js库,是一个精简的MVVM.MVVM模式是由经典的软件架构MVC衍生来的,当View(视图层)变化时,会自动更新到ViewModel(视图模型),反之亦然,View ...

  2. jQuery 源码分析(一) 代码结构

    jQuery是一个Javascript库,它支持链式操作方式,即对发生在同一个JQuery对象上的一组动作,可以直接接连写无需要重复获取对象.这一特点使得JQuery的代码无比优雅,而且有强大的选择器 ...

  3. Zepto源码分析(一)核心代码分析

    本文只分析核心的部分代码,并且在这部分代码有删减,但是不影响代码的正常运行. 目录 * 用闭包封装Zepto * 开始处理细节 * 正式处理数据(获取选择器选择的DOM) * 正式处理数据(添加DOM ...

  4. 一个普通的 Zepto 源码分析(二) - ajax 模块

    一个普通的 Zepto 源码分析(二) - ajax 模块 普通的路人,普通地瞧.分析时使用的是目前最新 1.2.0 版本. Zepto 可以由许多模块组成,默认包含的模块有 zepto 核心模块,以 ...

  5. Zepto源码分析(二)奇淫技巧总结

    Zepto源码分析(一)核心代码分析 Zepto源码分析(二)奇淫技巧总结 目录 * 前言 * 短路操作符 * 参数重载(参数个数重载) * 参数重载(参数类型重载) * CSS操作 * 获取属性值的 ...

  6. MySQL源码分析以及目录结构 2

    原文地址:MySQL源码分析以及目录结构作者:jacky民工 主要模块及数据流经过多年的发展,mysql的主要模块已经稳定,基本不会有大的修改.本文将对MySQL的整体架构及重要目录进行讲述. 源码结 ...

  7. MySQL源码分析以及目录结构

    原文地址:MySQL源码分析以及目录结构作者:jacky民工 主要模块及数据流经过多年的发展,mysql的主要模块已经稳定,基本不会有大的修改.本文将对MySQL的整体架构及重要目录进行讲述. 源码结 ...

  8. 一个普通的 Zepto 源码分析(三) - event 模块

    一个普通的 Zepto 源码分析(三) - event 模块 普通的路人,普通地瞧.分析时使用的是目前最新 1.2.0 版本. Zepto 可以由许多模块组成,默认包含的模块有 zepto 核心模块, ...

  9. 一个普通的 Zepto 源码分析(一) - ie 与 form 模块

    一个普通的 Zepto 源码分析(一) - ie 与 form 模块 普通的路人,普通地瞧.分析时使用的是目前最新 1.2.0 版本. Zepto 可以由许多模块组成,默认包含的模块有 zepto 核 ...

  10. ffplay源码分析2-数据结构

    ffplay是FFmpeg工程自带的简单播放器,使用FFmpeg提供的解码器和SDL库进行视频播放.本文基于FFmpeg工程4.1版本进行分析,其中ffplay源码清单如下: https://gith ...

随机推荐

  1. Python学习(三十八)—— Djago之Ajax

    转载自:http://www.cnblogs.com/yuanchenqi/articles/7638956.html 一.Ajax准备知识:json 什么是json? 定义: JSON(JavaSc ...

  2. Linux shell 脚本报错:/bin/bash^M: bad interpreter: No such file or directory

    今天遇到一个很诡异的问题,一直运行很正常的shell脚本失败了,只是昨天增加了一个参数而已. 报错信息: /bin/bash^M: bad interpreter: No such file or d ...

  3. JS对象与原型链

    每个函数都存在一个prototype的属性,然后这个属性值为一个对象,我们称之为原型对象 每个对象都存在着一个隐藏的属性"__proto__" 这个属性引用了创建这个对象的函数的p ...

  4. Ackerman

    Ackerman 递归算法 一 . 问题描述及分析 图1 二 . 代码实现 package other; import java.io.BufferedWriter; import java.io.F ...

  5. 解决sql_mode=only_full_group_by的问题

    1.mysql查询报错: ORDER BY clause is not in GROUP BY..this is incompatible with sql_mode=only_full_group_ ...

  6. Java数据库连接与查询

    9个步骤: 1.加载数据库驱动: 2.连接数据库: 3.创建语句statement: 5.创建sql语法字符串: 6.执行: 7.如果步骤6是执行新增.修改.删除操作那么返回的是影响的行数,如果是执行 ...

  7. 马昕璐 201771010118《面向对象程序设计(java)》第六周学习总结

    第一部分:理论知识学习部分 1.继承 继承:用已有类来构建新类的一种机制.当定义了一个新类继承了一个类时,这个新类就继承了这个类的方法和域,同时在新类中添加新的方法和域以适应新的情况. 继承是Java ...

  8. Codeforces Round #524 (Div. 2)

    A. Petya and Origamitime limit per test1 secondmemory limit per test256 megabytesinputstandard input ...

  9. linux中安装和配置 jdk

    01.去官网下载指定的jdk 02.使用xftp把下载好的文件 传递到 linux指定文件夹中03.进入指定的文件夹输入tar -zxvf 文件名称04.发现文件 05.进入文件cd jdk1.8.0 ...

  10. ASP.NET Core 问题排查:Request.EnableRewind 后第一次读取不到 Request.Body

    实际应用场景是将用户上传的文件依次保存到阿里云 OSS 与腾讯云 COS ,实现方式是在启用 Request.EnableRewind() 的情况下通过 Request.Body 读取流,并依次通过 ...