jQuery源码阅读(一)---jQuery源码整体架构

来源:互联网 发布:网络社会组织 编辑:程序博客网 时间:2024/05/17 09:06

之前用jQuery库写了两个小例子(结合Apache、PHP实现的简易聊天室以及音乐播放器),详见我的上两篇博客jQuery aJax技术以及PHP实现简单聊天室、 利用jQuery实现音乐播放器。为了更加深入了解jQuery库的架构以及巩固原生JS的基础和深度,决定刨一刨jQuery源码。

jQuery源码架构

首先,jQuery源码的整体构架如下:(此图来源于“jQuery技术内幕:深入解析jQuery架构设计与实现原理” 高云)
jQuey整体架构

  1. 入口模块
    jQuery的入口模块,主要是创建jQuery对象。这块其实是比较绕的,涉及到JS 原型的概念。
  2. 底层模块
    jQuery的底层模块主要包括一些工具方法,以及比较底层的,用的比较多的函数方法。比如onConflict(),isArray(),isFunction(),makkeArray()等等,建议大家可以看看jQuery API手册
  3. 功能模块
    这块应该是我们平时用jQuery库用的比较多的方法。比如aJax请求,动画,事件处理,样式设置与获取,属性设置与获取等等,这些方法都依赖于底层模块的工具方法和浏览器功能测试,主要用于浏览器检测,解决浏览器兼容问题。同时,功能模块不同的方法也依赖于底层模块的各个不同方法。
    源码结构如下:
(function (window, undefined){    //创建jQeury对象    var jQuery = function(){        var jQuery = function(selector, context){            return new jQuery.fn.init(selector, context);        }        jQuery.fn = jQuery.prototype = function(){            //原型上的方法,即所有jQuery对象都可以共享的方法和属性        }        jQuery.fn.init.prototype = jQuery.fn;        window.jQeury = window.$ = jQuery;    }();})(window);

入口模块

本篇博客主要想解析的是入口模块。
首先是一个立即执行函数

(function (window, undefined){    //创建jQeury对象})(window);

JS高程中函数-闭包这一章节就有讲到,原生JS中没有块级作用域的概念,但可以利用立即执行函数来模拟一个私有作用域。这样做是为了保证变量不被外面的变量影响。

另一方面,为什么要传window实参来执行函数呢?
window是顶级作用域,就算不传window参数进去,也同样可以访问到,但这就可能需要沿着作用域链一直去查找,导致时间比较长,而传入window参数之后,可以保证很快访问到。

下来在立即执行函数里面,是jQuery对象的创建。想想我们如何利用jQuery库来创建jQuery对象的?
直接$函数就能创建。

$("<div>jQuery源码</div>");

而在原生JS中,创建一个对象都是用new 和构造函数来实现的,那么看看jQuery源码中是如何做到的?

var jQuery = function( selector, context ) {            // The jQuery object is actually just the init constructor 'enhanced'        return new jQuery.fn.init( selector, context, rootjQuery );    }

它是利用jQuery.fn.init方法来作为构造函数创建jQuery对象的。创建任何一个对象时,都会有与之对应的一个原型对象。为了使得以这个构造函数创建的每一个对象都有相同的属性和方法,要将其加到能连到jQuery的原型链上。可能这句话听着不是很明白,下面画一个图可以直观的缕一下:
在看到上面new jQuery.fn.init( selector, context, rootjQuery )操作之后,我们脑海中应该会有下面这幅图的样子:

这里写图片描述
下来我们想,如何将真正利用jQuery.fn.init()构造的对象与jQuery对象联系?即每一个构造出来的对象都可以共享一些相同的方法和属性,JS中原型链的概念可以帮助解决。因此,有了下面这幅图:
这里写图片描述

源码上如何实现?

jQuery.fn = jQuery.prototype = function(){    init: function(selector, context, rootjQuery){        //创建jQuery对象    }    //当然还有别的方法和属性   }jQuery.fn.init.prototype = jQuery.fn;

这样就可以保证创建出的jQuery对象可以连到jQuery原型链上了。

为了更好的理解,可能用代码来说话最有说服力。我简单实现了这一段。一方面,这样看着比源码简要的多;另一方面,源码中init构造函数涉及到很多种情况,这里先不介绍,仅用一小段代码来代替。
jQuery.lh.1.1.0.js

(function(window, undefined){    var jQuery = (function(){        var jQuery = function(selector, context){            return new jQuery.fn.init(selector, context);        }        jQuery.fn = jQuery.prototype = {            constructor: jQuery,            init : function(selector, context){                this.person = selector;                this.name = context;                return this;            },            sayHello : function(){                console.log(this.name);            }        };        jQuery.fn.init.prototype = jQuery.fn;        window.$ = window.jQuery = jQuery;    })();})(window);

test.html

<!DOCTYPE html><html lang="en">    <head>        <meta charset="utf-8">        <script src="jQuery.lh.1.1.0.js"></script>        <script>            var o = jQuery("Hello", "Name");            var s = $("LiuHuan", "Cname");        </script>    </head>    <body>    </body></html>

浏览器中调试模式下,o和s都分别是对象,有person和name两个属性,并且有sayHello方法。
这里写图片描述

现在假设注释掉jQuery.fn.init.prototype = jQuery.fn;这一句代码,那么如果执行o.sayHello()会出现错误。原因就是因为实际用init构造的对象没有指回到jQuery对象的原型上,因此不能在原型链上访问到。
这里写图片描述