JavaScript組件模式深入淺出

來源:互聯網
上載者:User

“組件模式”是一種很常用的Javascript編碼模式。雖然已經被廣泛的應用,但是還有一些進階的用途沒有被關注。在這邊文章裡,我將針對幾個比較特別的話題進行簡單的闡述,其中的一些我認為應該是第一次被提及。

基礎

我們從一個簡單的組件模式開始說起,這個模式現在已經非常流行,最初是3年前由雅虎YUI的Eric Miraglia在其部落格中提出的。如果你已經對這個模式很熟悉了,那麼可以直接跳到“進階模式”一節。

匿名閉包(Anonymous Closures)

這是使得模式可以正常工作的語言基礎,也實在是javascript最為實用的特性之一。我們可以簡單的建立一個匿名函數,並且立刻執行它。所有運行在該函數裡的代碼叫做一個閉包,它提供了在整個應用程式生命週期中都有效資料隱私控制以及狀態儲存功能。

 
  1. (function () {        
  2. // ... all vars and functions are in this scope only          
  3. // still maintains access to all globals  
  4. }()); 

請注意()包圍了匿名函數,這是文法的需要,因為以function開頭的語句會被編譯器認為的函數的定義,而加上()就變為了建立了一個函數運算式。

全域匯入(Global Import)

JavaScript 有一個大家熟知的特性叫做implied globals,就是當使用一個變數的時候,解譯器會沿著範圍鏈一直向上尋找這個變數的定義,如果沒找到,這個變數就會自動成為一個全域(global)變數。這意味著在一個匿名閉包裡去建立一個全域變數變得非常簡單。

但是,這產生了很難管理的代碼問題,因為我們無法明顯的知道一個給定的檔案裡究竟哪些變數是全域的

(譯者註:如果允許這麼隱晦的寫法來建立全域變數,則代碼中使用的變數,很難知道是全域的還是局部的。)

幸運的是,我們的匿名函數提供了一個很簡單的修改來解決這個問題。我們傳入一個全域對象作為匿名函數的參數,我們利用這個參數匯入我們的變數到全域對象中,這樣做比隱式的聲明全域變數要乾淨和快速的多。下面是一個例子

 
  1. (function ($, YAHOO) {          
  2. // now have access to globals jQuery (as $) and YAHOO in this code  
  3. }(jQuery, YAHOO)); 

模組匯出(Module Export)

有時候我們不只是想定義一個模組,還想直接使用它,我們可以使用匿名函數的傳回值來實現。如果這麼做,就是實現了一個基本的“組件模式”,看下面的例子:

 
  1. var MODULE = (function () {          
  2. var my = {},privateVariable = 1;                 
  3.  function privateMethod() {                  
  4. // ...          
  5. }  
  6. my.moduleProperty = 1;         
  7.  my.moduleMethod = function () {                  
  8. // ...          
  9. };                  
  10. return my;  
  11. }()); 

要注意的是我們定義了一個全域群組件名字叫“MODULE”,他有兩個屬性:一個方法MODULE.moduleMethod以及一個屬性MODULE.moduleProperty。並且,它通過匿名函數所在的閉包維護了一組私人的內部狀態

(譯者註:privateVariable變數和privateMethod方法都是私人的,外部無法訪問)。

這樣的話,我們就可以用上面的模式簡單的匯入需要的全域變數。

進階模式(Advanced Patterns)

上面的模式已經可以解決應用中的許多問題,但是我們還可以在這個基礎上擴充出很多強大的,易擴充的結構。讓我們一個個看一下,繼續用我們的組件“MODULE”做例子

增益模式(Augmentation)

使用上面的模式的缺點和限制之一,就是整個模組必須在一個檔案裡。任何在大型項目中工作的人都會意識到能將代碼拆分成多個檔案帶來的價值。幸運的是,我們有一個不錯的解決方案叫做“增益組件”。首先,我們匯入組件,然後添加屬性,然後再將其匯出。下面是一個例子,為我們的MODULE組件添加增益特性:

 
  1. var MODULE = (function (my) {          
  2. my.anotherMethod = function () {                  
  3. // added method...          
  4. };          
  5. return my;  
  6. }(MODULE)); 

我們使用var關鍵字來保持一致性(儘管這不是必須的)。這段代碼執行過後,我們的組件就增加了一個公開的方法叫做MODULE.anotherMethod。這個增加的檔案也會同時保持它自己的私人狀態。

(譯者註:通過增益模式,一個雷被切分成獨立的檔案,每個檔案關注它內部需要的方法和屬性,並且當所有檔案合并執行後,可以成為一個完整的組件)

松耦合增益(Loose Augmentation)

我們上面的例子有一個缺點,就是需要MODULE模組先被建立,然後再執行子檔案中的增益。這是一種緊耦合的做法。javascript效能最佳化中通常的做法是指令碼的非同步載入,這樣我們就可以建立由多個檔案組成的易擴充的組件,並且他們可以以任何順序來載入,這叫做松耦合增益模式:

 
  1. var MODULE = (function (my) {          
  2. // add capabilities...                 
  3.  return my;  
  4. }(MODULE || {})); 

在這個模式中,var關鍵字是必須的。注意我們的import點(參數)會自動判斷組件是否存在,否則就建立一個空組件。這意味著你可以使用一些非同步載入工具比如LABjs來並行載入你所有的js組件,而不需要block頁面。

緊耦合增益(Tight Augmentation)

“松耦合增益”模式非常棒,不過它也有一些限制,最重要的一個就是無法重寫屬性。你也無法在初始化組件的時候使用其他檔案的內容(因為載入順序無法保證),“緊耦合增益模式”需要一個有序的載入順序,但是允許你重寫其他檔案的內容,這是一個簡單的例子:

 
  1. var MODULE = (function (my) {          
  2. var old_moduleMethod = my.moduleMethod;                  
  3. my.moduleMethod = function () {                  
  4. // method override, has access to old through old_moduleMethod...         
  5.  };                 
  6.  return my;  
  7. }(MODULE)); 

這裡我們重寫了 MODULE.moduleMethod,同時也儲存了舊的 MODULE.moduleMethod方法的引用(如果需要的話)

複製和繼承(Cloning and Inheritance)

 
  1. var MODULE_TWO = (function (old) {          
  2. var my = {}, key;                  
  3. for (key in old) {                  
  4. if (old.hasOwnProperty(key)) {                          
  5. my[key] = old[key];                  
  6. }          
  7. }                  
  8. var super_moduleMethod = old.moduleMethod;          
  9. my.moduleMethod = function () {                  
  10. // override method on the clone, access to super through super_moduleMethod        };                  
  11. return my;  
  12. }(MODULE)); 

這種模式也許是最沒有擴充性的選項了。它雖然看上去讓代碼更簡潔,但是帶來的是擴充性的損失。我們上面的代碼中,所有的屬性和方法都不是副本,他們以“一個對象2個引用”的方式存在。修改其中一個都會影響到另外一個。

跨檔案私人狀態(Cross-File Private State)

將組件分割到多個檔案中的另外一個限制,就是每個檔案只能訪問他自己的私人狀態,而沒有許可權訪問其他檔案的私人資料。這個問題可以解決,下面是一個“松耦合增益模式”的跨檔案訪問私人狀態的版本:

 
  1. var MODULE = (function (my) {          
  2. var _private = my._private = my._private || {},                  
  3. _seal = my._seal = my._seal || function () {                          
  4. delete my._private;                          
  5. delete my._seal;                          
  6. delete my._unseal;                  
  7. },                  
  8. _unseal = my._unseal = my._unseal || function () {                          
  9. my._private = _private;                         
  10.  my._seal = _seal;                          
  11. my._unseal = _unseal;                 
  12.  };                  
  13. // permanent access to _private, _seal, and _unseal                  
  14. return my;  
  15. }(MODULE || {})); 

現在任何檔案都可以在本地變數_private中設定屬性,並且會立即將設定結果反饋給其他檔案。一旦組件載入完成,應用程式應該調用 MODULE._seal()方法,這個方法會阻止外部程式訪問內部屬性_private。如果這個組件被其他檔案進行了擴充(增益),則可在載入新的檔案之前,調用_unseal()方法解鎖,然後在檔案載入完之後調用_seal()再次將類對外部屏蔽。

(譯者註:注意_unseal方法會在對象裡添加1個公開的屬性和2個公開方法,其他檔案就可以在my對象中隊private屬性進行擴充,然後利用_seal()方法刪除公開的屬性,保持對象的密封性)

這個模式是今天我在工作的時候想到的,我沒有在其他地方見過這種用法,我認為這是一種十分有用的模式。

子模組(Sub-modules)

Our final advanced pattern is actually the simplest. There are many good cases for creating sub-modules. It is just like creating regular modules:

我們最後的一個進階模式實際上是最簡單的,建立子模組有很多的好處,建立子模組就和建立普通模組一樣:

 
  1. MODULE.sub = (function () {          
  2. var my = {};         
  3.  // ...                  
  4. return my;  
  5. }()); 

雖然這很簡單,但我還是認為子模組應該包含進來,因為子模組擁有所有正常模組有的進階特性,包括增益和私人資料儲存。

結論

大多數進階模式都可以結合起來使用,如果是我的話,我比較喜歡把“松耦合增益”、“私人狀態儲存”、“子模式”結合起來使用

我這篇文字並沒有討論效能相關的內容,不過我想簡單的說一下:組件模式是對效能有好處的。因為它確實讓下載快了很多:使用“松耦合增益”模式可以讓指令碼並行的無阻塞載入,這加快了下載速度。整體初始化完成的時間也許比其他方法慢了一些,不過這是值得的。

作為結尾,這裡有一個子模組的例子,他動態載入自己到它的父模組中。這個模式允許整個模組並行的載入自己,以及自己的子模組。

譯文出自:刺客之家的部落格

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.