天天看點

jQuery技術内幕:深入解析jQuery架構設計與實作原理. 2.2 總體結構

<b>2.2 總體結構</b>

構造jquery對象子產品的總體源碼結構如代碼清單2-1所示。

代碼清單2-1 構造 jquery 對象子產品的總體源碼結構

 16 (function(

window, undefined ) {

// 構造 jquery 對象

 22    var jquery = (function() {

 25       var jquery = function( selector, context

) {

 27              return new jquery.fn.init(

selector, context, rootjquery );

 28          },

              // 一堆局部變量聲明

 97       jquery.fn = jquery.prototype = {

 98          constructor: jquery,

 99          init: function( selector, context,

rootjquery ) { ... },

              // 一堆原型屬性和方法

319       };

322       jquery.fn.init.prototype = jquery.fn;

324       jquery.extend = jquery.fn.extend =

function() { ... };

388       jquery.extend({

              // 一堆靜态屬性和方法

892       });

955       return jquery;

 957   

})();

// 省略其他子產品的代碼

9246     window.jquery = window.$ = jquery;

9266 })( window );

下面簡要梳理下這段源碼。

第16~9266行是最外層的自調用匿名函數,第1章中介紹過,當jquery初始化時,這個自調用匿名函數包含的所有javascript代碼将被執行。

第22行定義了一個變量jquery,第22~957行的自調用匿名函數傳回jquery構造函數并指派給變量jquery,最後在第9246行把這個jquery變量暴露給全局作用域window,并定義了别名$。

在第22~957行的自調用匿名函數内,第25行又定義了一個變量jquery,它的值是jquery構造函數,在第955行傳回并指派給第22行的變量jquery。是以,這兩個jquery變量是等價的,都指向jquery構造函數,為了友善描述,在後文中統一稱為構造函數jquery()。

第97~319行覆寫了構造函數jquery()的原型對象。第98行覆寫了原型對象的屬性constructor,使它指向jquery構造函數;第99行定義了原型方法jquery.fn.init(),它負責解析參數selector和context的類型并執行相應的查找;在第27行可以看到,當我們調用jquery構造函數時,實際傳回的是jquery.fn.init()的執行個體;此外,還定義了一堆其他的原型屬性和方法,例如,selector、length、size()、toarray()等。

第322行用jquery構造函數的原型對象jquery.fn覆寫了jquery.fn.init()的原型對象。

第324行定義了jquery.extend()和jquery.fn.extend(),用于合并兩個或多個對象的屬性到第一個對象;第388~892行執行jquery.extend()在jquery構造函數上定義了一堆靜态屬性和方法,例如,noconflict()、isready、readywait、holdready()等。

看上去代碼清單2-1所述的總體源碼結構有些複雜,下面把疑問和難點一一羅列,逐個分析。

1)為什麼要在構造函數jquery()内部用運算符new建立并傳回另一個構造函數的執行個體?

通常我們建立一個對象或執行個體的方式是在運算符new後緊跟一個構造函數,例如,newdate()會傳回一個date對象;但是,如果構造函數有傳回值,運算符new所建立的對象會被丢棄,傳回值将作為new表達式的值。

jquery利用了這一特性,通過在構造函數jquery()内部用運算符new建立并傳回另一個構造函數的執行個體,省去了構造函數jquery()前面的運算符new,即我們建立jquery對象時,可以省略運算符new直接寫jquery()。

為了拼寫更友善,在第9246行還為構造函數jquery()定義了别名$,是以,建立jquery對象的常見寫法是$()。

2)為什麼在第97行執行jquery.fn

= jquery.prototype,設定jquery.fn指向構造函數jquery()的原型對象jquery.prototype?

jquery.fn是jquery.prototype的簡寫,可以少寫7個字元,以友善拼寫。

3)既然調用構造函數jquery()傳回的jquery對象實際上是構造函數jquery.fn.init()的執行個體,為什麼能在構造函數jquery.fn.init()的執行個體上調用構造函數jquery()的原型方法和屬性?例如,$('#id').length和$('#id').size()。

在第322行執行jquery.fn.init.prototype = jquery.fn時,用構造函數jquery()的原型對象覆寫了構造函數jquery.fn.init()的原型對象,進而使構造函數jquery.fn.init()的執行個體也可以通路構造函數jquery()的原型方法和屬性。

4)為什麼要把第25~955行的代碼包裹在一個自調用匿名函數中,然後把第25行定義的構造函數jquery()作為傳回值指派給第22行的jquery變量?去掉這個自調用匿名函數,直接在第25行定義構造函數jquery()不也可以嗎?去掉了不是更容易閱讀和了解嗎?

去掉第25~955行的自調用匿名函數當然可以,但會潛在地增加構造jquery對象子產品與其他子產品的耦合度。在第25~97行之間還定義了很多其他的局部變量,這些局部變量隻在構造jquery對象子產品内部使用。通過把這些局部變量包裹在一個自調用匿名函數中,實作了高内聚低耦合的設計思想。

5)為什麼要覆寫構造函數jquery()的原型對象jquery.prototype?

在原型對象jquery.prototype上定義的屬性和方法會被所有jquery對象繼承,可以有效減少每個jquery對象所需的記憶體。事實上,jquery對象隻包含5種非繼承屬性,其餘都繼承自原型對象jquery.prototype;在構造函數jquery.fn.init()中設定了整型屬性、length、selector、context;在原型方法.pushstack()中設定了prevobject。是以,也不必因為jquery對象帶有太多的屬性和方法而擔心會占用太多的記憶體。

關于構造函數、原型、繼承等基礎知識,請查閱相關的基礎類書籍。

繼續閱讀