jQuery源碼 架構分析

來源:互聯網
上載者:User

每一個架構都有一個核心,所有的結構都是基於這個核心之上,結構建立好了之後,剩下的就是功能的堆砌。

jQuery的核心就是從HTML文檔中匹配元素並對其操作。

就跟一座大樓一樣,讓我們一步一步瞭解這座大廈的基石和結構。


1.建構函式

2.鏈式文法

3.選取器

4.擴充性


一、建構函式

  我們知道類是物件導向編程的一個重要概念,它是對事物的最高抽象,它是一個模型。通過執行個體化一個類,我們可以建立一個執行個體。

javascript本身沒有類的概念,只有原型prototype,prototype是什麼呢?它是為建構函式設定的一個屬性,這個屬性包含一 個對象(簡稱prototype對象)。這個對象中包含了執行個體對象共用的屬性(properties)和方法(methods)。有了這麼一個屬性,我們 就可以讓所有的執行個體共用某些東西,來實現物件導向中類的概念。

下面用一個例子來解釋一下,連結--------------

因此jQuery利用原型繼承來實作類別的

 1 var jQuery = function(){} 2  3 jQuery.prototype = { 4     //擴充的原型對象 5 } 6 //由於prototype名稱太長,我們可以另起一名fn 7 jQuery.fn = jQuery.prototype = { 8     //擴充的原型對象 9 }10 /********************我是分割線************************/11 //同理jQuery也可以用別名$來代替,因此代碼可以寫成12 var $ = jQuery = function(){};13 jQuery.fn = jQuery.prototype = {14     //擴充的原型對象15     jquery : "1.7.2",16     size : function(){17         return this.length;18     }19 }

那我們如何調用它嗎?

1 var my$ = new $();//執行個體化2 console.log(my$.jquery);//1.7.23 console.log(my$.size());//undefined

但是jQuery並不是這麼調用的,它類似$().jquery 這種形式。

也就是說jQuery沒用new執行個體化,而是直接調用jQuery()函數,然後後面跟jQuery的原型方法。怎麼實現呢?

 1 var $ = jQuery = function(){ 2     return  new jQuery(); 3 };     4 jQuery.fn = jQuery.prototype = { 5      6     jquery : "1.7.2", 7     size : function(){ 8         return this.length; 9     }10 }11 12 console.log($().jquery);13 console.log($().size());

如果按照上面的做法會出錯,記憶體溢出,因為建立執行個體的時候循環參考導致出錯。

我們需要返回一個執行個體,我們知道在new一個對象的時候,this指向new的執行個體,執行個體擷取了prototype的屬性方法。

因此我們可以用Factory 方法建立一個執行個體,把這個方法放在jQuery.prototype 的原型對象當中,然後在jQuery函數中返回這個原型方法的調用。

 1 var $ = jQuery = function(){ 2     return  jQuery.fn.init(); 3 };     4 jQuery.fn = jQuery.prototype = { 5     init: function(){ 6         console.log(this); 7         return this;//返回執行個體的引用 8     }, 9     jquery : "1.7.2",10     size : function(){11         return this.length;12     }13 }14 console.log($().jquery);15 console.log($().size());

console中會看到this對象是 jQuery的一個執行個體。

init()方法返回的是this關鍵字,該關鍵字引用的是jQuery的執行個體,如果在init()中繼續使用this關鍵字,也就是將init函數視為一個構造器,this又是如何處理呢?

var $ = jQuery = function(){    return  jQuery.fn.init();};    jQuery.fn = jQuery.prototype = {    init: function(){        this.length = 2;        this.test = function(){            return this.length;        }        return this;    },    jquery : "1.7.2",    length:0,    size : function(){        return this.length;    }}console.log($().jquery);console.log($().test()); //2 ? 0 ?console.log($().size()); //2 ? 0 ?

  返回的都是2,可以看到,this關鍵字引用了init函數範圍所在的對象,此時它訪問length屬性時,返回的為2。this關鍵字也能訪問上級對象jQuery.fn對象的範圍,所以返回1.7.2,而調用size方法時,返回的是2而不是0。

這種設計思路很容易破壞範圍的獨立性,對jQuery架構可能產生消極影響,因此jQuery通過執行個體化init初始化類型來分割範圍的

1 var $ = jQuery = function(){2     return new jQuery.fn.init();3 };    

  這樣就可以把init()構造函器中的this和jQuery.fn對象中的this關鍵字隔離開來。避免混淆。但是這種方法帶來的另一個問題是無法訪問jQuery.fn 的對象的屬性和方法。

Object [object Object] has no method 'size'.

如何做到既能分割初始化建構函式與jQuery原型對象的範圍,又能夠在返回執行個體中訪問jQuery原型對象呢?

jQuery架構巧妙地通過原型傳遞解決了這個問題

1 jQuery.fn.init.prototype = jQuery.fn;//使用jQuery原型對象覆蓋init原型對象

  這樣 new jQuery.fn.init() 建立的新對象擁有init構造器的prototype原型對象的方法,通過改變prototype指標的指向,使其指向jQuery類的prototype,這樣創造出來的對象就繼承了jQuery.fn原型對象定義的方法。

二、擴充性


  jQuery 自訂擴充方法用的extend () 函數

1 jQuery.extend = jQuery.fn.extend = function() {2     //code    3 }

在講源碼之前,先說一下什麼是拷貝,淺拷貝,深拷貝。

我們知道js 種不同的資料類型

* 基本類型:按值傳遞  (undefined,NULL,boolean,String,Number)
* 參考型別:傳遞記憶體位址 Object


/* 深度拷貝,所有的元素和屬性完全clone,並與原引用對象完全獨立,複製後的對象與原對象再也沒有任何關係,也就是當你拷貝完成後,原對象值有任何更改,都不會影響到我們複製後那個對象的值*/

所以我們在進行深拷貝(clone)的時候,注意將複製對象中的每一個值,而不是引用,換句話說,就是採用遞迴的方法淺拷貝對象。

1.淺拷貝

 1 var clone = _.clone = function(obj){ 2     //不是對象直接放回返回值 3     if (typeof obj != 'object') return obj; 4     var result; 5     //數組用slice方法 不改變原數組 6     if(Object.prototype.toString.call(obj)==="[Object Array]"){ 7         result =  obj.slice(); 8     }else{ 9         //對象 for遍曆10         result = {};11         for(var name in obj){12             result[name] = object[name];13         }14     }15     return result;16 }

2.深拷貝

 1 var _deepClone = function(source){ 2     if(source===null) return null; 3     var result; 4     if(source instanceof Array){ 5         result = []; 6         //如果是數組,遞迴調用 7         for(var i = 0;i

3.jQuery 的實現

 1 jQuery.extend = jQuery.fn.extend = function() { 2     //所有使用的的變數最好最好在函數定義的最前就寫下來,原因與ECMA有關 詳解 3  4     var options, name, src, copy, copyIsArray, clone, 5         target = arguments[0] || {}, 6         i = 1, 7         length = arguments.length, 8         deep = false; 9 10     // 判斷是否為深拷貝|淺拷貝11     if ( typeof target === "boolean" ) {12         deep = target;13         target = arguments[1] || {};    //返回的目標對象14         // 遍曆的時候跳過 deep | target 參數15         i = 2;16     }17 18     // 如果初始值不為對象 且不是一個函數則置空,比如一個string ""置為{};19     if ( typeof target !== "object" && !jQuery.isFunction(target) ) {20         target = {};21     }22 23     // 如果只有自己一個參數,而沒有被複製繼承的參數,則返回自己本身24     if ( length === i ) {25         target = this;26         --i;27     }28 29     for ( ; i < length; i++ ) {30         // 處理值為null undefined情況31         if ( (options = arguments[ i ]) != null ) {32             // 繼承對象options33 34             for ( name in options ) {35                 src = target[ name ];       //原對象中對應的值36                 copy = options[ name ];     //需要拷貝的對象中對應的值37 38                 // 防止陷入死迴圈,如果原對象本身就等於需要拷貝的對象中的那值(o),            //在對o遍曆的時候就把自己重新遍曆賦值了一遍39                 if ( target === copy ) {40                     continue;41                 }42 43                 //在 Array和Object的情況,且deep為true和傳進對象有值(true)的情況下,遞迴調用本身方法進行深拷貝44                 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {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                     target[ name ] = jQuery.extend( deep, clone, copy );54                     // 如果不是上述情況,則進行淺拷貝,我們在執行jQuery.extend({})或jQuery.fn.extend({})的時候都是執行的這個方法,但卻搞不明白為什麼一個是對JQuery類的自訂擴充,一個是JQuery對象的自訂擴充,那麼這裡的target究竟代表什麼呢,我們看下面的例子55                 } else if ( copy !== undefined ) {56                     target[ name ] = copy;57                 }58             }59         }60     }61 62     // 返回修改過後的target63     return target;64 };

注意:雖然jQuery.extend = jQuery.fn.extend 它們是一個方法,但是它們的具體作用是不一樣的,因為this的指向不同。

function jQuery() {}//使用字面量的方式建立原型對象,這裡{}就是對象,是Object,new Object就相當於{}jQuery.fn = jQuery.prototype = {constructor : jQuery,//強制指向jQueryname : 'Lee', age : 100,run : function () {return this.name + this.age + '運行中...';}};jQuery.extend = jQuery.fn.extend = function(){var option = arguments[0] ;for(var v in option){this[v] = option[v];}return this;};var jquery = new jQuery();document.write("

"+jQuery.extend({add:function(){alert("aaaa");}})+"

");document.write("

"+jQuery.fn.extend({minu:function(){alert("bbbb");}})+"

");jQuery.add();jquery.minu();
this列印結果

function jQuery() {}

[object Object]


在建構函式那個模組我們看到

jQuery .extend 的this 是jQuery類本身,在jQuery類上添加(對象,方法)

jQuery.fn.extend 的this是jQuery的原型,在jQuery原型上添加(對象,方法),那麼JQuery對象本身也會具有哪些方法

jQuery.fn = jQuery.prototype

四、來一個簡化版的

 1 var _deepClone = function(source){ 2     if(source===null) return null; 3     var result; 4     if(source instanceof Array){ 5         result = []; 6         //如果是數組,遞迴調用 7         for(var i = 0;i

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.