由於JavaScript開發變得越來越普遍,命名空間和依賴管理更加難以處理,前赴後繼的程序員們提出來很多的解決方案,本文將探討一些經典的方案,並描述這些方案解決了哪些問題。
爲什麼需要模塊系統
作爲開發人員,我們一定知道封裝和依賴。在實際的項目開發中,我們通常會引入項目依賴,如果沒有封裝機制,這可能會導致代碼間的各種衝突,所以我們在看一些C語言的源碼庫的時候,經常會看到各種前綴:
#ifndef MYLIB_INIT_H
#define MYLIB_INIT_H
enum mylib_init_code {
mylib_init_code_success,
mylib_init_code_error
};
enum mylib_init_code mylib_init(void);
//(...)
#endif //MYLIB_INIT_H
封裝可以有效解決代碼衝突。但在Web端的js開發中,僅僅做到封裝是不夠的,我們還要確保各依賴模塊能按照正確的順序加載和執行。
我們通過Backbone.js的一個例子來說明手動控制依賴的加載順序:
<html lang="en">
<head>
<meta charset="utf-8">
<title>Backbone.js Todos</title>
<link rel="stylesheet" href="todos.css"/>
</head>
<body>
<script src="../../test/vendor/json2.js"></script>
<script src="../../test/vendor/jquery.js"></script>
<script src="../../test/vendor/underscore.js"></script>
<script src="../../backbone.js"></script>
<script src="../backbone.localStorage.js"></script>
<script src="todos.js"></script>
</body>
<!-- (...) -->
</html>
隨着現代JavaScript的開發越來越複雜,依賴管理也變得越來越困難,引入模塊系統是大勢所趨。
特殊的模塊封裝
在介紹現代模塊系統之前,我們先介紹一種特殊的編程模式,在一定程度上它也可以解決很多JavaScript應用的模塊管理。
var myRevealingModule = (function () {
var privateVar = "Ben Cherry",
publicVar = "Hey there!";
function privateFunction() {
console.log( "Name:" + privateVar );
}
function publicSetName( strName ) {
privateVar = strName;
}
function publicGetName() {
privateFunction();
}
// Reveal public pointers to
// private functions and properties
return {
setName: publicSetName,
greeting: publicVar,
getName: publicGetName
};
})();
myRevealingModule.setName( "Paul Kinlan" );
這是通過函數作用域來實現私有變量的封裝,有函數return語句暴露對外訪問的接口。在上面的例子中,函數內部不一定需要通過var來聲明變量,函數也無需立即執行,命名函數也可以作爲一個模塊來使用。
在很長一段時間,這種方式可以很好地實現模塊封裝,但它不能解決依賴管理的問題,同時也無法引入其它模塊,除非在函數內容通過eval執行外部JavaScript。
特點
- 實現簡單,原生支持;
- 在單個文件中定義多個模塊。
缺陷
- 無法導入其他文件中的模塊;
- 手動處理依賴管理;
- 無法實現依賴的異步加載;
- 無法有效處理依賴間的循環引用;
- 無法靜態分析源碼依賴。