命名空間
JavaScript本身中沒有提供命名空間機制,所以為了避免不同函數、對象以及變數名對全域空間的汙染,通常的做法是為你的應用程式或者庫建立一個唯一的全域對象,然後將所有方法與屬性添加到這個對象上。
複製代碼 代碼如下:/* BEFORE: 5 globals */
// constructors
function Parent() {}
function Child() {}
// a variable
var some_var = 1;
// some objects
var module1 = {};
module1.data = {a: 1, b: 2};
var module2 = {};
/* AFTER: 1 global */
// global object
var MYAPP = {};
// constructors
MYAPP.Parent = function() {};
MYAPP.Child = function() {};
// a variable
MYAPP.some_var = 1;
// an object
MYAPP.modules = {};
// nested objects
MYAPP.modules.module1 = {};
MYAPP.modules.module1.data = {a: 1, b: 2};
MYAPP.modules.module2 = {};
代碼清單1 : 傳統命名空間模式
在這段代碼中,你建立了一個全域對象MYAPP,並將其他所有對象、函數作為屬性附加到MYAPP上.
通常這是一種較好的避免命名衝突的方法,它被應用在很多項目中,但這種方法有一些缺點。
1.需要給所有需要添加的函數、變數加上首碼。
2.因為只有一個全域對象,這意味著一部分代碼可以肆意地修改全域對象而導致其餘代碼的被動更新。
全域構造器
你可以用一個全域構造器,而不是一個全域對象,我們給這個構造器起名為Sandbox(),你可以用這個構造器建立對象,你還可以為構造器傳遞一個回呼函數作為參數,這個回呼函數就是你存放代碼的獨立沙箱環境。 複製代碼 代碼如下:new Sandbox(function(box){
// your code here...
});
代碼清單2:沙箱的使用
讓我們給沙箱添加點別的特性
1.建立沙箱時可以不使用'new'操作符
2.Sandbox()構造器接受一些額外的配置參數,這些參數定義了產生對象所需模組的名稱,我們希望代碼更加模組化。
擁有了以上特性後,讓我們看看怎樣初始化一個對象。
代碼清單3顯示了你可以在不需要‘new'操作符的情況下,建立一個調用了'ajax'和'event'模組的對象.
複製代碼 代碼如下:Sandbox(['ajax', 'event'], function(box){
// console.log(box);
});
代碼清單3:以數組的形式傳遞模組名 複製代碼 代碼如下:Sandbox('ajax', 'dom', function(box){
// console.log(box);
});
代碼清單4:以獨立的參數形式傳遞模組名
代碼清單5顯示了你可以把萬用字元'*'作為參數傳遞給構造器,這意味著調用所有可用的模組,為了方便起見,如果沒有向構造器傳遞任何模組名作為參數,構造器會把'*'作為預設參數傳入.
複製代碼 代碼如下:Sandbox('*', function(box){
// console.log(box);
});
Sandbox(function(box){
// console.log(box);
});
代碼清單5:調用所用可用模組
代碼清單6顯示你可以初始化沙箱對象多次,甚至你可以嵌套它們,而不用擔心彼此間會產生任何衝突. 複製代碼 代碼如下:Sandbox('dom', 'event', function(box){
// work with dom and event
Sandbox('ajax', function(box) {
// another sandboxed "box" object
// this "box" is not the same as
// the "box" outside this function
//...
// done with Ajax
});
// no trace of Ajax module here
});
代碼清單6:嵌套的沙箱執行個體
從上面這些樣本可以看出,使用沙箱模式,通過把所有代碼邏輯包裹在一個回呼函數中,你根據所需模組的不同產生不同的執行個體,而這些執行個體彼此互不干擾獨立的工作著,從而保護了全域命名空間。
現在讓我們看看怎樣實現這個Sandbox()構造器.
添加模組
在實現主構造器之前,讓我們看看如何向Sandbox()構造器中添加模組。
因為Sandbox()構造器函數也是對象,所以你可以給它添加一個名為'modules'的屬性,這個屬性將是一個包含一組索引值對的對象,其中每對索引值對中Key是需要註冊的模組名,而Value則是該模組的入口函數,當構造器初始化時當前執行個體會作為第一個參數傳遞給入口函數,這樣入口函數就能為該執行個體添加額外的屬性與方法。
在代碼清單7中,我們添加了'dom','event','ajax'模組。
複製代碼 代碼如下:Sandbox.modules = {};
Sandbox.modules.dom = function(box) {
box.getElement = function() {};
box.getStyle = function() {};
box.foo = "bar";
};
Sandbox.modules.event = function(box) {
// access to the Sandbox prototype if needed:
// box.constructor.prototype.m = "mmm";
box.attachEvent = function(){};
box.dettachEvent = function(){};
};
Sandbox.modules.ajax = function(box) {
box.makeRequest = function() {};
box.getResponse = function() {};
};
代碼清單7:註冊模組
實現構造器
代碼清單8描述了實現構造器的方法,其中關鍵的幾個要點:
1.我們檢查this是否為Sandbox的執行個體,若不是,證明Sandbox沒有被new操作符調用,我們將以構造器方式重新調用它。
2.你可以在構造器內部為this添加屬性,同樣你也可以為構造器的原型添加屬性。
3.模組名稱會以數組、獨立參數、萬用字元‘*'等多種形式傳遞給構造器。
4.請注意在這個例子中我們不需要從外部檔案中載入模組,但在諸如YUI3中,你可以僅僅載入基礎模組(通常被稱作種子(seed)),而其他的所有模組則會從外部檔案中載入。
5.一旦我們知道了所需的模組,並初始化他們,這意味著調用了每個模組的入口函數。
6.回呼函數作為參數被最後傳入構造器,它將使用最新產生的執行個體並在最後執行。 複製代碼 代碼如下:function Sandbox() {
// turning arguments into an array
var args = Array.prototype.slice.call(arguments),
// the last argument is the callback
callback = args.pop(),
// modules can be passed as an array or as individual parameters
modules = (args[0] && typeof args[0] === "string") ?
args : args[0],
i;
// make sure the function is called
// as a constructor
if (!(this instanceof Sandbox)) {
return new Sandbox(modules, callback);
}
// add properties to 'this' as needed:
this.a = 1;
this.b = 2;
// now add modules to the core 'this' object
// no modules or "*" both mean "use all modules"
if (!modules || modules === '*') {
modules = [];
for (i in Sandbox.modules) {
if (Sandbox.modules.hasOwnProperty(i)) {
modules.push(i);
}
}
}
// init the required modules
for (i = 0; i < modules.length; i++) {
Sandbox.modules[modules[i]](this);
}
// call the callback
callback(this);
}
// any prototype properties as needed
Sandbox.prototype = {
name: "My Application",
version: "1.0",
getName: function() {
return this.name;
}
};
代碼清單8:實現Sandbox構造器
原文來自:Stoyan Stefanov - JavaScript Patterns Part 7:The Sandbox Pattern