jQuery 源碼分析 8: 回頭看jQuery的構造器(jQuery.fn,jQury.prototype,jQuery.fn.init.prototype的分析)


  在第一篇jQuery源碼分析中,簡單分析了jQuery對象的構造過程,里面提到了jQuery.fn、jQuery.prototype、jQuery.fn.init.prototype的關系。

  從代碼中可以看出,這三者其實都是等價的,都是指向了jQuery.prototype,但這又是為什么呢?為什么要這么繞?一個jQuery.prototype不就解決問題了嗎?帶着這些疑問,再一次來看看其中的精妙之處。
 

  jQuery構造器

  jQuery使用非常方便,其中一個原因就是我們在調用的時候並不需要使用關鍵字new來創造一個jQeury對象,直接使用jQuery("#id")或$(".class")就可輕松得到一個新的jQuery對象。原因就是jQuery使用工廠方法,利用構造器創造一個新的jQuery對象並返回,省去了用戶的new操作。

 

    一般構造器

     一般情況下,我們在JavaScript中創建一個類的時候,會這樣做:
1 var $ = jQuery = function() {
2     return new jQuery();
3 }
4 jQuery.prototype = {
5     jquery: "1.11"
6 };
7 console($().jquery);

  上面的代碼有些奇怪,因為一個構造器返回利用它自身創建了一個新的對象並返回,這樣形成了一個死循環,產生棧溢出的異常。因此,上面的代碼是錯誤的代碼。

 返回原型方法 jQuery.fn.init

 

  為了解決循環,就必須在jQuery.prototype中定義的一個構造器,這就是jQuery.fn.init了。
  jQeury.fn實際上就是jQuery.prototype的別名,我認為這個別名的目的就為了說明prototype上掛載的都是jQuery類的公有方法,同時也縮減了引用jQuery.prototype時要書寫的代碼長度。
     jQuery.fn.init能夠返回jQuery.fn這個原型方法,代碼是:
 1 var $ = jQuery = function() {
 2     return jQuery.fn.init();
 3 }
 4 jQuery.fn = jQuery.prototype = {
 5     init: function(){
 6         return this;
 7     },
 8     jquery: "1.11"
 9 }
10 console.log($().jquery);
11 console.log($());       // 將jQuery.prototype打印出來, Object { init: jQuery.prototype.init(), jquery: "1.11"}    

  雖然這個方法解決了嵌套為問題,也將jQuery的原型方法傳遞給了jQuery對象,但它是將一個完整的jQuery.prototype暴露處理,jQuery的任何對this關鍵字的操作,實際上就是直接在jQuery.prototype上進行操作,那么jQuery.fn的完整性很容易就被破壞了,jQuery對象之間也可能產生不可預估的影響。

     如:
1 console.log($().jquery);     // 1.11
2 $().jquery = '2.1';
3 console.log($().jquery);     // 2.1

返回一個jQuery.fn.init對象

  為了避免直接返回jQuery.fn,我們需要借鑒一開始使用關鍵字new的方法,通過new創建一個新Oject,改變了this所指向的對象,從而避開對jQuery.fn的直接暴露。
 1 var $ = jQuery = function() {
 2     return new jQuery.fn.init();
 3 }
 4 jQuery.fn = jQuery.prototype = {
 5     init: function(){
 6         this.num = 2015;
 7         return this;
 8     },
 9     jquery: "1.11"
10 }
11 
12 console.log($().num);          // 2015
13 console.log($().jquery);       // undefined
14 console.log($());                 // Object {num: 2015}

  但此時問題來了,new jQuery.fn.init()所返回的新對象並沒有繼承jQuery.fn,因為jQuery.fn.init.prototype僅僅是指向了一個function對象的原型,並不包含jQuery.fn。這時,是徹底地將jQuery.fn給封鎖了起來。

讓jQuery.fn.init.prototype指向jQuery.fn

     既然jQuery.fn.init.prototype只是指向了Object.prototype,那么我們只需要改變它的指向,讓它指向jQuery.fn不就好了嗎?
     
 1 var $ = jQuery = function(x) {
 2     return new jQuery.fn.init(x);
 3 }
 4 jQuery.fn = jQuery.prototype = {
 5     init: function(x){
 6         this.num = x || 0;
 7         return this;
 8     },
 9     jquery: "1.11"
10 }
11 jQuery.fn.init.protoytpe = jQuery.fn;
12 
13 console.log($(2015).num);           // 2015
14 console.log($(2015).jquery);        // 1.11
15 console.log($(2016).num);           // 2016
16 console.log($(2016).__proto__);     // Object { init: jQuery.prototype.init(), jquery: "1.11" }

  顯然,jQuery.fn.init這個工廠方法完全奏效了,$()返回的每個對象,都擁有獨立的內部變量,也共享jQuery.fn上的公有方法和屬性。

  總結  

  到這里,相信這個三個特殊的名字存在的意義就非常明了了。
  • jQuery.prototype,掛載jQuery對象的原型方法;
  • jQuery.fn是jQuery.prototype的別名,標注jQuery.prototype的意義且縮短代碼書寫長度,避免混淆(像jQuery.prototype.init.prototype = jQuery.prototype這行代碼,能把人看昏了)方便使用;
  • jQuery.fn.init.prototype,則是為了讓jQuery.fn.init這個工廠方法能夠繼承jQuery.fn上的原型方法。
  雖然三個特殊的名字都代表了同樣一個東西,但他們的意義並不相同,因此需要特別注意這一點。

  參考書:朱印宏 《jQury內核詳解與實戰》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM