完全不封裝時
最開始的js是很簡單的,它並不承擔很複雜的功能,於是所有的變量和函數都寫在全局作用域中(Global)。這樣一開始沒有什麼問題,但是當JS的代碼越來越多,後來慢慢出現插件或多個JS文件時就有問題了,變量和函數的名稱很容易衝突。
function foo(){
//...
}
function bar(){
//...
}
利用JS對象模擬命名空間
利用JS對象,將相關的方法和屬性封裝一層,這樣可以避免很多同功能函數的衝突。
var MYAPP = {
foo: function(){},
bar: function(){}
}
MYAPP.foo();
但是這樣只能做到簡單的減少全局中變量的衝突,關於安全方面完全沒有幫助,還是可以隨便覆蓋和訪問。
利用函數的塊級作用域
隱藏局部變量
塊級作用域裏的變量外面是訪問不到的,利用這一點可以將我們的功能封裝在一個匿名函數中,只通過返回值暴露出我們想暴露出的接口。
var Module = (function(){
var _private = "safe now";
var foo = function(){
console.log(_private)
}
return {
foo: foo
}
})()
Module.foo();
Module._private; // undefined
利用函數傳參添加依賴
就是醬咯,這種封裝的方式已經把現代模塊化代碼上的要素基本都包含了。
獨立性是模塊的重要特點,模塊內部最好不與程序的其他部分直接交互。
爲了在模塊內部調用全局變量,必須顯式地將其他變量輸入模塊。
var Module = (function($){
var _$body = $("body"); // we can use jQuery now!
var foo = function(){
console.log(_$body); // 特權方法
}
// Revelation Pattern
return {
foo: foo
}
})(jQuery)
Module.foo();
繼承或拓展模塊
利用傳參的特性,可以繼承已有的模塊:
var module1 = (function (mod){
mod.m3 = function () {
//...
};
return mod;
})(module1);
模塊管理
當我們有多個js的時候我們會寫很多的script標籤,但是這樣不僅增加了HTTP請求,而且難以維護,因爲執行順序是從上至下的,所以被依賴的需要放在前面,文件多了就比較尷尬了。
CommonJS
定義模塊
上下文提供了exports對象用於導出當前模塊的方法或者變量,並且它是唯一的導出的出口。在模塊中,還在 一個module對象,它代表模塊自身,exports是module的屬性。將方法掛載在exports上作爲屬性即可。
exports.add = function () {
var sum = 0,
i = 0,
args = arguments,
l = args.length;
while (i < l) {
sum += args[i++];
}
return sum;
};
使用模塊
使用時就在要使用的文件中引用:
var selfModule = require('selfModule');
console.log(selfModule.add(13,3));
在引用的過程中,根據具體實現的不同,使用的模塊標識也不同,絕對路徑是一般都支持的。
同步加載
在你require的時候,是同步加載這個模塊的,也就是說,這個模塊中的代碼執行完纔會繼續執行主文件中下面的代碼。
可以試下下面這個例子:
var EXE_TIME = 2;
(function(second){
var start = +new Date();
while(start + second*1000 > new Date()){}
})(EXE_TIME)
console.log("2000ms executed");
var timeout = require('E:\\Git\\NodeTest\\timeout.js');
console.log('done!');
這種阻塞式的加載在服務器端是沒有問題的,比如node。但是在瀏覽器端,阻塞式加載並不是很理想。
AMD
定義模塊
在這裏,一個磁盤文件應該只定義一個模塊
如果一個模塊僅含值對,沒有任何依賴,則在define()中定義這些值對就好了:
define({
color: "black",
size: "unisize"
});
如果一個模塊沒有任何依賴,但需要一個做setup工作的函數,則在define()中定義該函數,並將其傳給define():
define(function () {
//Do setup work here
return {
color: "black",
size: "unisize"
}
});
如果模塊存在依賴:則第一個參數是依賴的名稱數組;第二個參數是函數,在模塊的所有依賴加載完畢後,該函數會被調用來定義該模塊,因此該模塊應該返回一個定義了本模塊的object。依賴關係會以參數的形式注入到該函數上,參數列表與依賴名稱列表一一對應。
define(["./cart", "./inventory"], function(cart, inventory) {
//return an object to define the "my/shirt" module.
return {
color: "blue",
size: "large",
addToCart: function() {
inventory.decrement(this);
cart.add(this);
}
}
}
);
使用模塊
require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
// some code here
});
模塊的加載
加載模塊時默認要加載的模塊和加載它們的文件在同一個目錄下,但是如果不是你可以通過require.config來配置。
CMD
CMD的定義模塊部分與AMD有區別,更加接近於CommonJS。
define(function(require, exports, module){
var a = require("a");
a.doSomething();
var b = require("b");
b.doSomething(); // 依賴就近,延遲執行
})
兼容模塊規範
爲了能將一個模塊可以同時運行在使用不同包管理的地方,我們可以這樣定義模塊:
(function (name, definition) {
// 檢測上文環境是否爲AMDCMD
var hasDefine = typeof define === 'function',
// 檢查上文環境是否爲Node
hasExports = typeof module !== 'undefined' && module.exports;
if (hasDefine) {
// AMD環境CMD環境
define(definition);
} else if (hasExports) {
// 定義爲通用Node模塊
module.exports = definition();
} else {
// 將模塊的執行結果掛在window變量中,在瀏覽器中this指向window對象
this[name] = definition();
}
})('math', function () {
var interface = {};
interface.add=function () {
var sum = 0,
i = 0,
args = arguments,
l = args.length;
while (i < l) {
sum += args[i++];
}
return sum;
};
return interface;
});