js菜鳥進階-jQuery源碼分析(1)-基本架構

導讀:javascript

本人JS菜鳥一枚,爲增強代碼美觀和編程思想。因此來研究下jQuery,有須要進階JS的同窗很適合閱讀此文!我是邊看代碼(jquery2.2.1),邊翻「javascript高級程序設計」寫的,有不少基本知識點我都寫了書本對應的章節。有分析得很差的還請各位多多指教,更正!css

但願個人分析對你們有所幫助,謝謝!html

1、代碼構成


(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方法屬性都是在這個回調裏實現.裏的最後一段代碼,就是對外開放方法的接口。
java

2、jQuery類的構建


開始入正題了,嘎嘎。。
首先咱們來看幾個jQ的一經常使用場景方法:jquery

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

這麼一看jQuery不就是一個Object對象裏面添加了幾個方法麼,其實確實是的,只不過他是一個Function類型的對象。看下面代碼:編程

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

是的jQuery就是一個Function對象,那麼咱們就有幾個問題了:安全

  一、在咱們印象中jQuery不是一個類庫麼?
  二、JS中的類不是用構造函數來仿造的嗎?
  三、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改爲一個典型的工廠模式看看; ide

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又引用了他的原型。

一、爲啥不用一個單獨的局部變量,而是放在了jQuery的prototype上?
二、fn 他這fn也沒啥特殊意思,就一個引用。感受這倆步有點多餘?
這兩點 望大神解釋!!

3、方法拓展(方法擴展接口)


咱們把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的基本架構就是這樣的了,總結
  一、全部的工做都是在一個匿名函數內執行,保證了命名空間的安全
  二、使用了工廠模式構建的類
  三、jQuery有一個叫extend的方法,jQuery全部方法屬性都是使用此方法擴展出來的
  四、使用的方法分兩大類,全局方法類和實例方法類,他們都有本身的對應的擴展入口


本文爲原創文章,轉載請註明出處!

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

相關文章
相關標籤/搜索