在JavaScript中創建對象是非常簡單的,你可以使用對象字面量(object literal)或者使用構造函數(方法)(constructor)。
在這章裏,我們會超出這些,看到一些額外的對象創建模式。
JavaScript語言是簡單而直接的,通常沒有特別的語法實現你在其它語言中使用的功能,比如命名空間(namespaces)、模塊(modules)、包(packages),私有屬性(private properties)和靜態變量(static members)。接下來,我們會去實現這些常見的模式,或者使用其他方式代替,或者比較二者的不同。
我們會看到命名空間(namespacing),依賴聲明(dependency declaration),模塊模式(module pattern)和沙箱模式(sandbox patterns),這些會幫助組織和構建你的程序代碼並且減少潛在的全局變量。討論的主題還包括private或者享有特權(privileged)的成員變量,static或者private static的成員,對象常量,鏈式編程,使用特殊的方法的構造函數。
命名空間模式(Namespace Pattern)
// BEFORE: 5 globals
// Warning: antipattern
// constructors
function Parent() {}
function Child() {}
// a variable
var some_var = 1;
// some objects
var module1 = {};
module1.data = {a: 1, b: 2};
var module2 = {};
你可以重構這種類型的代碼通過爲你的程序創建一個全局對象,比如叫做:MYAPP,並且將你所有的函數和變量作爲這個全局變量的屬性:// AFTER: 1 global
// global object
var MYAPP = {};
// constructors
MYAPP.Parent = function () {};
MYAPP.Child = function () {};
// a variable
MYAPP.some_var = 1;
// an object container
MYAPP.modules = {};
// nested objects
MYAPP.modules.module1 = {};
MYAPP.modules.module1.data = {a: 1, b: 2};
MYAPP.modules.module2 = {};
對於全局命名空間對象的名字,你可以選擇你的程序或者類庫的名字,你的域名,或者公司的名字。通常程序猿都會遵循約定,全局變量名全大寫,所以更容易被代碼的讀者發現。(但也要記住全部大寫通常也被用來命名常量)- 需要更多的輸入;每個變量和函數都要添加一個前綴,會增加需要下載的代碼量。
- 只有一個全局變量意味着任何地方的代碼都可以修改這個全局變量,其它地方都會受到影響。
- 很長的嵌套名稱會降低屬性查找速度。
通用的命名空間函數(General Purpose Namespace Function)
// unsafe
var MYAPP = {};
// better
if (typeof MYAPP === "undefined") {
var MYAPP = {};
}
// or shorter
var MYAPP = MYAPP || {};
你可以看到這些添加的檢查會很快導致大量重複代碼。比如:如何你想定義MYAPP.modules.module2,你講不得不進行三次檢查,每個你定義的屬性或對象都需要一次;這就是爲什麼你需要一個方便的可重用的函數來具體維護命名空間。讓我們將這個函數叫做namespace() 並且向這樣去使用:
// using a namespace function
MYAPP.namespace('MYAPP.modules.module2');
// equivalent to:
// var MYAPP = {
// modules: {
// module2: {}
// }
// };
接下來的例子是命名空間函數的實現。這個實現是非破壞性的,意味着如何一個命名空間已經存在,那麼它不會被重新創建:var MYAPP = MYAPP || {};
MYAPP.namespace = function (ns_string) {
var parts = ns_string.split('.'),
parent = MYAPP,
i;
// strip redundant leading global
if (parts[0] === "MYAPP") {
parts = parts.slice(1);
}
for (i = 0; i < parts.length; i += 1) {
// create a property if it doesn't exist
if (typeof parent[parts[i]] === "undefined") {
parent[parts[i]] = {};
}
parent = parent[parts[i]];
}
return parent;
};
這個實現可以這樣使用:// assign returned value to a local var
var module2 = MYAPP.namespace('MYAPP.modules.module2');
module2 === MYAPP.modules.module2; // true
// skip initial `MYAPP`
MYAPP.namespace('modules.module51');
// long namespace
MYAPP.namespace('once.upon.a.time.there.was.this.long.nested.property');