[1]反模式 [2]字面量 [3]IIFE [4]IIFE傳參 [5]命名空間 ...
前面的話
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等來處理