js菜鸟进阶-jQuery源码分析(1)-基本架构

导读:

本人JS菜鸟一枚,为加强代码美观和编程思想。所以来研究下jQuery,有需要进阶JS的同学很适合阅读此文!我是边看代码(jquery2.2.1),边翻“javascript高级程序设计”写的,有很多基本知识点我都写了书本对应的章节。有分析得不好的还请各位多多指教,更正!

希望我的分析对大家有所帮助,谢谢!

一、代码构成


(function(global, factory){
    if ( typeof module === "object" && typeof module.exports === "object" ) {
    //模块化环境
    }else{
        factory( global );
    }
})(typeof window !== "undefined" ? window: this, function(window, noGlobal) {
    //回调函数

    if ( typeof noGlobal === strundefined ) {
        window.jQuery = window.$ = jQuery;
    }
    return jQuery;
});

首先一个优美的JS库都会是在一个匿名函数里去写自己的代码对吧,jQuery也不列外。这其实只要把代码格式化一看,就一目了然。
这个匿名函数接受的是两个参数,global(当期执行作用域链的对象),factory(回调)
  匿名函数 : 他本身是做了一个初始化方法的判断,判断当前JS使用环境是不是采用的模块化开发。如果是再做一些相应的逻辑处理(模块化就不多介绍了,可以自己到网上查询),否则直接执行回调factory并把当前执行的作用域对象当参数传递过去。
  回调函数: factory 所有的JQ方法属性都是在这个回调里实现.里的最后一段代码,就是对外开放方法的接口。

二、jQuery类的构建



开始入正题了,嘎嘎。。
首先我们来看几个jQ的一常用场景方法:

$.get(); $.post(); $.extend();//场景一

这么一看jQuery不就是一个Object对象里面添加了几个方法么,其实确实是的,只不过他是一个Function类型的对象。看下面代码:

$("#id").html(); $(".class").show();//场景二

是的jQuery就是一个Function对象,那么我们就有几个问题了:

  1、在我们印象中jQuery不是一个类库么?
  2、JS中的类不是用构造函数来仿造的吗?
  3、JS构造函数不是都是用new操作符来实例化的么,为什么jQuery不需要使用new来实例化???
要实现无new操作,我们想到可以使用工厂模式。(工厂模式可以参阅 “高级-第六章 面向对象的程序设计” )接下来我们先看看这个function的代码:

// Define a local copy of jQuery
jQuery = function(selector, context) {

    // The jQuery object is actually just the init constructor ‘enhanced‘
    // Need init if jQuery is called (just allow error to be thrown if not included)
    return new jQuery.fn.init(selector, context);
}

是的他的这段代码使用的就是JS中典型的工厂模式,工厂模式是不需new操作符的,我们来把jQuery改成一个典型的工厂模式看看;

var jQuery = function(selector,context){
    // jQuery("#id")这种使用场景我们留到选择器的时候再分析

    var o = new Object();
    o.get = function(){
        console.log("get");
    };
    o.post = function(){
        console.log("post");
    };
    return o;
    //或者
    retrun {
        get : function(){}
        ,post : function(){}
    }
    //对jQuery中返回一个 new 实例有疑问的参阅(第5章 引用类型)
};

jQuery.get(); //get
jQuery.post(); //post

嗯,如果改成上面这种方式似乎也可以是吧,但我们仔细看下代码。是不是每次使用我们都需要创建一个新对象,也在内存堆里开辟一个新的内存空间了(堆、栈知识参阅 第4章 变量、作用域和内存问题)那样就影响性能了,所以我们再改写下:

var fn = {
    get : function(){}
    ,name : "cH"
    ,post : function(){}
    ,init : function(){
        //这是一个内部构造函数
        this.age = 18;
        console.log(this.age);
        console.log(this.name);
    }
};

var jQuery = function(selector,context){
    return new fn.init();
};
jQuery();

首先解释下为什么又返回是 return new init 而不是 return fn:
  我们如果直接返回fn的话那它就会造成对象引用而且是单实例的。而我们用new实例的话,我们每次使用jQuery都是一个新的实例,这样与其他的实例他就是没有任何干扰的。还有就是考虑到$(".calss").hide().html()这种应用场景,我们也必须考虑使用多实例。
  那我们每次调用不又是new一个实例,他不是又会造成性影响了么?是的,但是我们把fn拿出来了,所以每次new只是在栈里拷贝了一份这个对象的指针,并不像开始一样是在堆里新建了一个对象。所以照样减少了性能开销。
OK,我们运行下代码,发现打this.name属性是undefied,为啥呢?看这个new fn.init(),我们new的init是个实例,在这个实例里的this指向是当前实例的,而name、get这些是fn这个局部变量的,所以this.name当然是undefied咯。那我们再改下代码:

var fn = {
    get : function(){
        console.log("get");
    }
    ,name : "cH"
    ,post : function(){}
    ,init : function(){
        //这是一个内部构造函数
        this.age = 18;
        console.log(this.age);
        console.log(this.name);
    }
};
//我们把内部构造函数的原型继承fn的属性 (继承 第6章 6.2)
fn.init.prototype = fn;

var jQuery = function(selector,context){
    return new fn.init();
};
jQuery();
jQuery().get();

这下是正常的了,我们把init这个构造函数的原型继承了fn这个对象。这样他的属性就继承过来了。
我们知道有个这样的属性 jQuery.fn
所以再改下代码:

var jQuery = function(selector,context){
    return new jQuery.fn.init();
};
jQuery.fn = {
    get : function(){
        console.log("get");
    }
    ,name : "cH"
    ,post : function(){}
    ,init : function(){
        //这是一个内部构造函数
        this.age = 18;
        console.log(this.age);
        console.log(this.name);
    }
};
jQuery.fn.init.prototype = jQuery.fn;

这样一看代码就跟源码差不多了,我们对应再来看下jQuery源码:

//构建jQuery类
// Define a local copy of jQuery
jQuery = function(selector, context) {

    // The jQuery object is actually just the init constructor ‘enhanced‘
    // Need init if jQuery is called (just allow error to be thrown if not included)
    return new jQuery.fn.init(selector, context);
}
//定义我们分析时的局部变量
jQuery.fn = jQuery.prototype = {
    get :...
    ,name : ....
    ,post : ....
}
//定义fn.init方法,内部构造函数
init = jQuery.fn.init = function(selector, context, root) {
    var match, elem;

    // HANDLE: $(""), $(null), $(undefined), $(false)
    if (!selector) {
        return this;
    }
}
//内部构造函数的原型继承
init.prototype = jQuery.fn;

我们发现他定义的局部变量是定义在了jQuery的prototype上了,而fn又引用了他的原型。

1、为啥不用一个单独的局部变量,而是放在了jQuery的prototype上?
2、fn 他这fn也没啥特殊意思,就一个引用。感觉这俩步有点多余?
这两点 望大神解释!!

三、方法拓展(方法扩展接口)



我们把jQuery的使用方法归成了俩大类。

//全局方法类
$.get();
$.post();
...

//当前实例类
$("#id").show();
$("#id").css();
...

全局方法类:
  扩展入口:$.extend()
  这个扩展就是直接给我们第一的jQuery构造器添加了一些静态方法  

$.extend({meSay : function(){
       console.log("meSay")
 }});
 $.meSay();//meSay
  //这个扩展就相当于这样 jQuery.meSay = function...;

当前实例类:

  扩展入口:$.fn.extend()
  而这个,还记得有个fn的属性吧,而他的所有属性方法是被继承到了当前实例的原型里去了的。所以我们这个扩展就只是给当前实例做的一个扩展。

$.fn.extend({
    meSay : function(){
        console.log("meSay");
    }
});
$("#id").meSay();//meSay
//这个扩展相当于 jQuery.fn.meSay = function...;

看到jQuery的源码,jQuery.extend和jQuery.fn.extend其实是同一个方法的不同引用

jQuery.extend = jQuery.fn.extend = function() {}
//jQuery.extend 对jQuery本身的属性和方法进行了扩展
//jQuery.fn.extend 对jQuery.fn的属性和方法进行了扩展

extend的实现源码

 1 jQuery.extend = jQuery.fn.extend = function() {
 2     var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {},
 3     i = 1,
 4     length = arguments.length,
 5     deep = false;
 6
 7     // Handle a deep copy situation
 8     if (typeof target === "boolean") {
 9         deep = target;
10
11         // Skip the boolean and the target
12         target = arguments[i] || {};
13         i++;
14     }
15
16     // Handle case when target is a string or something (possible in deep copy)
17     if (typeof target !== "object" && !jQuery.isFunction(target)) {
18         target = {};
19     }
20
21     // Extend jQuery itself if only one argument is passed
22     if (i === length) {
23         target = this;
24         i--;
25     }
26
27     for (; i < length; i++) {
28
29         // Only deal with non-null/undefined values
30         if ((options = arguments[i]) != null) {
31
32             // Extend the base object
33             for (name in options) {
34                 src = target[name];
35                 copy = options[name];
36
37                 // Prevent never-ending loop
38                 if (target === copy) {
39                     continue;
40                 }
41
42                 // Recurse if we‘re merging plain objects or arrays
43                 if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) {
44
45                     if (copyIsArray) {
46                         copyIsArray = false;
47                         clone = src && jQuery.isArray(src) ? src: [];
48
49                     } else {
50                         clone = src && jQuery.isPlainObject(src) ? src: {};
51                     }
52
53                     // Never move original objects, clone them
54                     target[name] = jQuery.extend(deep, clone, copy);
55
56                     // Don‘t bring in undefined values
57                 } else if (copy !== undefined) {
58                     target[name] = copy;
59                 }
60             }
61         }
62     }
63
64     // Return the modified object
65     return target;
66 };

jQuery.extend = jQuery.fn.extend = function() {

这个extend函数就是一个升级的深拷贝函数,jQuery内部的方法属性也都是使用此方法扩展出来的。我们看下他的使用场景:

$.extend({"meName" : "cHjQuery"});
$.meName // cHjQuery

var d = {name : "d"};
$.extend(d,{age:15});
d //{name :"d",age:15};

var c = $.extend({},{name:"c",age:15});
c //{name :"c",age:15};
//还有第个参数为true的场景,可以查询jQuery API

jQuery的基本架构就是这样的了,总结
  1、所有的工作都是在一个匿名函数内执行,保证了命名空间的安全
  2、使用了工厂模式构建的类
  3、jQuery有一个叫extend的方法,jQuery所有方法属性都是使用此方法扩展出来的
  4、使用的方法分两大类,全局方法类和实例方法类,他们都有自己的对应的扩展入口

本文为原创文章,转载请注明出处!

http://www.cnblogs.com/hrw3c/p/5304849.html

时间: 2024-10-16 03:09:36

js菜鸟进阶-jQuery源码分析(1)-基本架构的相关文章

jQuery源码分析-01总体架构

1. 总体架构 1.1自调用匿名函数 self-invoking anonymous function 打开jQuery源码,首先你会看到这样的代码结构: (function( window, undefined ) { // jquery code })(window); 1.这是一个自调用匿名函数.在第一个括号内,创建一个匿名函数:第二个括号,立即执行 2.为什么要创建这样一个“自调用匿名函数”呢? 通过定义一个匿名函数,创建了一个“私有”的命名空间,该命名空间的变量和方法,不会破坏全局的命

菜鸟学jQuery源码(一)

整个jQuery是一个自调用的匿名函数: 1 (function(global, factory) { 2 if (typeof module === "object" && typeof module.exports === "object") { 3 module.exports = global.document ? 4 factory(global, true) : 5 function(w) { 6 if (!w.document) { 7

菜鸟学jQuery源码(前言)

前言 相信任何一名前端开发人员或者是前端爱好者都对jQuery不陌生.jQuery简单易用,功能强大,特别是拥有良好的浏览器兼容性,大大降低了前端开发的难度,使得前端开发变得“平易近人起来”.自从本人用了jQuery,顿时感觉到人生再也不是灰色的了,又能够快乐的工作了. 不过在每天码得飞起的同时,我也对jQuery充满好奇,所以也特意的去查了一下资料.现在网上和书店里面有非常多的资料对jQuery源码从各种角度进行解析,大多都是对jQuery进行总结.归纳从上往下的分析.不过本人作为一名刚毕业的

jQuery源码分析系列(33) : AJAX中的前置过滤器和请求分发器

jQuery1.5以后,AJAX模块提供了三个新的方法用于管理.扩展AJAX请求,分别是: 1.前置过滤器 jQuery. ajaxPrefilter 2.请求分发器 jQuery. ajaxTransport, 3.类型转换器 ajaxConvert 源码结构: jQuery.extend({ /** * 前置过滤器 * @type {[type]} */ ajaxPrefilter: addToPrefiltersOrTransports(prefilters), /** * 请求分发器 *

Jquery源码分析

1.概述 jQuery是一个非常优秀的Js库,与prototype,YUI,Mootools等众多的Js类库相比,它剑走偏锋,从web开发最实用的角度出发,抛除了一些中看但不实用的东西,为开发者提供一个短小精悍的类库.由于其个短小精悍,使用简单方便,性能相对高效.众多的开发者都选择Jquery来进行辅助的web开发. 在使用jquery时开发,我们也会时常碰到许多的问题,但是jquery的代码很晦涩,难起看懂,当开发时出现了问题,看不懂源码,不知道如何去排错. John Resig,Jquery

jquery源码分析(二)——结构

再来复习下整体架构: jQuery源码分析(基于 jQuery 1.11 版本,共计8829行源码) (21,94)                定义了一些变量和函数jQuery=function(){} (96,280)        给jQuery添加一些方法和属性,jQuery.fn=jQuery.prototype(285,347)        extend:        jQuery的一些继承方法        更容易进行后续的扩展                       

jQuery源码分析-jQuery中的循环技巧

Js代码   作者:nuysoft/JS攻城师/高云 QQ:47214707 EMail:[email protected] 声明:本文为原创文章,如需转载,请注明来源并保留原文链接. 前记:本文收集了jQuery中出现的各种遍历技巧和场景 Js代码   // 简单的for-in(事件) for ( type in events ) { } Js代码   // 缓存length属性,避免每次都去查找length属性,稍微提升遍历速度 // 但是如果遍历HTMLCollection时,性能提升非常

jQuery源码分析系列(38) : 队列操作

Queue队列,如同data数据缓存与Deferred异步模型一样,都是jQuery库的内部实现的基础设施 Queue队列是animate动画依赖的基础设施,整个jQuery中队列仅供给动画使用 Queue队列 队列是一种特殊的线性表,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队).队列的特点是先进先出(FIFO-first in first out),即最先插入的元素最先被删除. 为什么要引入队列? 我们知道代码的执行流有异步与同步之分,例如 var a

jQuery源码分析系列(34) : Ajax - 预处理jsonp

上一章大概讲了前置过滤器和请求分发器的作用,这一章主要是具体分析每种对应的处理方式 $.ajax()调用不同类型的响应,被传递到成功处理函数之前,会经过不同种类的预处理(prefilters). 预处理的类型取决于由更加接近默认的Content-Type响应,但可以明确使用dataType选项进行设置.如果提供了dataType选项, 响应的Content-Type头信息将被忽略. 有效的数据类型是text, html, xml, json,jsonp,和 script. dataType:预期