標籤:java
java有類檔案、Python有import關鍵詞、Ruby有require關鍵詞、C#有using關鍵詞、PHP有include和require、CSS有@import關鍵詞,但是對ES5版本的javascript來說,javascript通過script標籤引入代碼的方式顯得雜亂無章,語言自身毫無組織和約束能力,人們不得不用命令空間等方式人為地約束代碼,以求達到安全和易用的目的。本文將詳細介紹javascript中的模組組織
反模式
反模式(Anti-Pattern)指沒有使用任何模組系統
簡單地,把不同的函數(以及選項組的變數)放在一起,就算是一個模組
650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;float:left;border:none;" />
function m1(){ //... } function m2(){ //... }
650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;float:left;border:none;" />
上面的函數m1()和m2(),組成一個模組。使用的時候,直接調用就行了。
這種做法的缺點很明顯:"汙染"了全域變數,無法保證不與其他模組發生變數名衝突,而且模組成員之間看不出直接關係
字面量
為瞭解決上面的缺點,可以把模組寫成一個字面量,所有的模組成員都放到這個對象裡面
650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;float:left;border:none;" />
var module1 = new Object({ _count : 0, m1 : function (){ //... }, m2 : function (){ //... } });
650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;float:left;border:none;" />
上面的函數m1()和m2(),都封裝在module1對象裡。使用的時候,就是調用這個對象的屬性
module1.m1();
但這種寫法會暴露所有模組成員,內部狀態可被外部改寫。比如,外部代碼可以直接改變內部計數器的值
module1._count = 5;
IIFE
使用"立即執行函數"(Immediately-Invoked Function Expression,IIFE)可以達到不暴露私人成員的目的
650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;float:left;border:none;" />
var module1 = (function(){ var _count = 0; var m1 = function(){ //... }; var m2 = function(){ //... }; return { m1 : m1, m2 : m2 }; })();
650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;float:left;border:none;" />
使用上面的寫法,外部代碼無法讀取內部的_count變數
console.info(module1._count); //undefined
IIFE傳參
如果一個模組需要繼承另一個模組,則需要IIFE傳參
650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;float:left;border:none;" />
var module1 = ( function (mod){ mod.m3 = function () { //... }; return mod; })(window.module1 || {});
650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;float:left;border:none;" />
命名空間
如果採用IIFE的方法,隨著模組的增多,仍然汙染了全域環境。
而命名空間(Namespace)可以通過只暴露類似於一個‘namespace‘的全域變數,來實現所有模組的聲明,進而解決全域環境的汙染問題
650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;float:left;border:none;" />
//math.jsnamespace(‘math‘, [], function(){ function add(a, b) { return a + b; } function sub(a, b) { return a - b; } return { add: add, sub: sub }})//calculator.jsnamespace(‘calculator‘, [‘math‘], function(m){ var action = ‘add‘; function compute(a,b) { return m[action](a, b); } return { compute: compute }})
650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;float:left;border:none;" />
650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;float:left;border:none;" />
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;})()
650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;float:left;border:none;" />
最後
雖然,使用命名空間可以解決全域環境汙染的問題,但是卻無法解決模組依賴管理的問題
如所示,module2依賴於module1和module3,則代碼如下
650) this.width=650;" src="http://images2015.cnblogs.com/blog/740839/201705/740839-20170512163838676-1698856628.png" style="border:none;margin-top:20px;margin-bottom:20px;" />
<script src="module1.js"></script><script src="module3.js"></script><script src="module2.js"></script>
但,如果模組組織如下所示
650) this.width=650;" src="http://images2015.cnblogs.com/blog/740839/201705/740839-20170512164300832-421694311.png" style="border:none;margin-top:20px;margin-bottom:20px;" />
甚至,如下所示
650) this.width=650;" src="http://images2015.cnblogs.com/blog/740839/201705/740839-20170512164314582-1695611926.png" style="border:none;margin-top:20px;margin-bottom:20px;" />
這時,手動地處理模組之間的依賴關係就不現實了,需要使用AMD、CMD、ES6 MODULE等來處理
實現javascript下的模組組織