zoukankan      html  css  js  c++  java
  • 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了。

  • 相关阅读:
    Self Host 使用 Exceptionless 实时监控程序运行日志服务
    Asp.Net Core 集成 Hangfire 配置使用 Redis 存储
    阿里云负载不支持 WebSocket 协议与 WSS 和 Nginx 配置问题
    使用 Scrapyd 管理部署 Scrapy 的一些问题
    Ubuntu 安装配置最新版 PostgreSQL
    Anaconda 科学计算环境与包的管理
    Linux 部署 ASP.NET Core 的一些问题记录
    关于 IdentityServer 部署到生产环境相关问题踩坑记录
    推荐一个 MYSQL 的命令行的客户端 MYCLI
    可能是 2017 最全的机器学习开源项目列表
  • 原文地址:https://www.cnblogs.com/hughes5135/p/10494960.html
Copyright © 2011-2022 走看看