文章出處

前面的話

  java有類文件、Python有import關鍵詞、Ruby有require關鍵詞、C#有using關鍵詞、PHP有include和require、CSS有@import關鍵詞,但是對ES5版本的javascript來說,javascript通過script標簽引入代碼的方式顯得雜亂無章,語言自身毫無組織和約束能力,人們不得不用命令空間等方式人為地約束代碼,以求達到安全和易用的目的。本文將詳細介紹javascript中的模塊組織

 

反模式

  反模式(Anti-Pattern)指沒有使用任何模塊系統

  簡單地,把不同的函數(以及記錄狀態的變量)放在一起,就算是一個模塊

  function m1(){
    //...
  }
  function m2(){
    //...
  }

  上面的函數m1()和m2(),組成一個模塊。使用的時候,直接調用就行了。

  這種做法的缺點很明顯:"污染"了全局變量,無法保證不與其他模塊發生變量名沖突,而且模塊成員之間看不出直接關系

 

字面量

  為了解決上面的缺點,可以把模塊寫成一個字面量,所有的模塊成員都放到這個對象里面

  var module1 = new Object({
    _count : 0,
    m1 : function (){
      //...
    },
    m2 : function (){
      //...
    }
  });

  上面的函數m1()和m2(),都封裝在module1對象里。使用的時候,就是調用這個對象的屬性

module1.m1();

  但這種寫法會暴露所有模塊成員,內部狀態可被外部改寫。比如,外部代碼可以直接改變內部計數器的值

module1._count = 5;

 

IIFE

  使用"立即執行函數"(Immediately-Invoked Function Expression,IIFE)可以達到不暴露私有成員的目的

  var module1 = (function(){
    var _count = 0;
    var m1 = function(){
      //...
    };
    var m2 = function(){
      //...
    };
    return {
      m1 : m1,
      m2 : m2
    };
  })();

  使用上面的寫法,外部代碼無法讀取內部的_count變量

console.info(module1._count); //undefined

 

IIFE傳參

  如果一個模塊需要繼承另一個模塊,則需要IIFE傳參

  var module1 = ( function (mod){
    mod.m3 = function () {
      //...
    };
    return mod;
  })(window.module1 || {});

 

命名空間

  如果采用IIFE的方法,隨著模塊的增多,仍然污染了全局環境。

  而命名空間(Namespace)可以通過只暴露類似于一個'namespace'的全局變量,來實現所有模塊的聲明,進而解決全局環境的污染問題

//math.js
namespace('math', [], function(){
  function add(a, b) { return a + b; }
  function sub(a, b) { return a - b; }
  return {
    add: add,
    sub: sub
  }
})

//calculator.js
namespace('calculator', ['math'], function(m){
  var action = 'add';
  function compute(a,b) {
    return m[action](a, b);
  }
  return {
    compute: compute
  }
})
var namespace = (function(){
    //緩存所有模塊
    var cache = {};
    function createModule(name/*模塊名*/,deps/*依賴列表*/,definition/*定義*/){
        //如果只有模塊名,則直接輸出
        if(arguments.length === 1){
            return cache[name];
        }
        //取得所有模塊的依賴
        deps = deps.map(function(depName){
            return namespace(depName); 
        })
        //初始化模塊并返回
        cache[name] = definition.apply(null,deps);

        return cache[name];
    }
    return createModule;
})()

 

最后

  雖然,使用命名空間可以解決全局環境污染的問題,但是卻無法解決模塊依賴管理的問題

  如下圖所示,module2依賴于module1和module3,則代碼如下

<script src="module1.js"></script>
<script src="module3.js"></script>
<script src="module2.js"></script>

  但,如果模塊組織如下所示

  甚至,如下所示

  這時,手動地處理模塊之間的依賴關系就不現實了,需要使用AMD、CMD、ES6 MODULE等來處理


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()