JavaScript創建對象的幾種重要模式
一、工廠模式
1. 代碼示例
function person(name, age) {
var p = new object();
p.name = name;
p.age = age;
p.sayName = function() {
console.log(this.name);
};
return p;
}
var person1 = person(‘Bonnie‘, 26);
var person2 = person(‘Summer‘, 24);
2. 優點
解決了創建多個相似對象的問題。
3. 缺點
沒有解決對象識別的問題。
二、 構造函數模式
1. 代碼示例
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function() {
console.log(this.name);
}
}
var person1 = new Person(‘Bonnie‘, 26);
var person2 = new Person(‘Summer‘, 24);
牢記:構造函數在不返回值的情況下,默認會返回新對象的實例(構造函數模式)。 如果在構造函數內部末尾添加一個return語句,可以重寫調用構造函數時返回的值(寄生構造函數模式)。
2.優點
創建自定義的構造函數意味著可以將它的示例標誌為一種特定的類型(執行person1 instanceof Person為true)。這就是它比工廠模式更勝一籌的地方。
3.缺點
每個方法都要在每個實例上創建一遍。也就是說,使用構造函數模式創建的每個實例都包含著各自獨有的同名函數。然而,創建兩個完成同樣任務的同名函數並沒有必要。
可以像這樣使構造函數創建的每個實例都引用一個方法:
function Person(name, age) { this.name = name; this.age = age; this.sayName = sayName; } function sayName() { console.log(this.name); }
但是這樣帶來的問題是被構造函數的不同實例共同引用的這個方法存在於全局作用域中,這一方面破壞了全局作用域,另一方面也破壞了該自定義類型的封裝性。
三、 原型模式
1. 代碼示例
function Person() {
}
Person.prototype.name = "Bonnie";
Person.prototype.age = 26;
Person.prototype.sayName = function() {
console.log(this.name)
}
var person1 = new Person();
var person2 = new Person();
2. 優點
可以讓所有對象實例共享它所包含的屬性和方法。
3. 缺點
首先,它省略了為構造函數傳遞初始化參數這一環節,這樣所有實例默認都會取得相同的屬性值。
更嚴重的是,其共享的本質對於引用類型的屬性值(如數組)是非常不適合的:對於修改一個實例上引用類型的屬性值也會修改另一個實例上的這個屬性值。不過,其共享本質對於函數非常合適,因為方法本來就是共用的。而其對於基本值的屬性也比較合適,因為可以通過在實例上添加同名的屬性名來覆蓋原型中的屬性。
詳見我的另一篇博客《原型與原型鏈》 的"一、創建對象的重要模式:原型模式"。
四、 組合使用構造函數模式和原型模式
1.代碼示例
function Person(name, age) {
this.name = name;
this.age = age;
this.friends = [‘Spring‘, ‘Huiyun‘];
}
Person.prototype.sayName = function() {
console.log(this.name);
}
var person1 = person(‘Bonnie‘, 26);
var person2 = person(‘Summer‘, 24);
2. 優點
實例屬性都在構造函數中實現,共享的屬性和方法在原型中定義,是使用最廣、認同度最高的一種創建自定義類型的方式。也是默認方式。
五、動態原型模式
1. 代碼示例
function Person(name, age) {
this.name = name;
this.age = age;
if (typeof this.sayName != ‘function‘) {
Person.prototype.sayName = function() {
console.log(this.name);
}
}
}
2. 優點
只有在某方法不存在的情況下,才將該方法添加到原型上。添加到原型上這段代碼只會在初次調用構造函數時才會執行。對原型所做的修改會立刻反映到所有實例中。
註意:不能使用對象字面量重寫原型,因為如果已經創建了實例,那麽重寫原型會切斷現有實例和新原型之間的聯系。
六、寄生構造函數模式
1. 代碼示例
function Person(name, age) {
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function() {
console.log(this.sayName);
}
return o;
}
var person1 = new Person(‘Bonnie‘, 26);
除了使用new操作符並把包裝的函數叫做構造函數之外,該模式跟工廠模式是一模一樣的。構造函數在不返回值的情況下默認返回新對象實例。而通過在構造函數內部末尾添加return語句,可以重寫調用構造函數時返回的值。
2. 優點
該模式適於為特殊對象(如Array)創建具有新屬性、新方法的實例。也就是說適於在某些已有的特殊類型(如Array)的基礎上創建新的特殊類型(如SpecialArray)。
例如可以在Array的基礎上,創建具有額外方法的數組:
function SpecialArray() {
var o = new Array();
o.push.apply(o, arguments);
o.getPipedStr = function() {
return this.join(‘|‘);
}
return o;
}
var colors = new SpecialArray(‘red‘, ‘blue‘, ‘green‘);
console.log(colors);// ["red", "blue", "green", getPipedStr: ?]
console.log(colors.getPipedStr);//"red|blue|green"
3. 缺點
該模式返回的對象與構造函數以及構造函數的原型之間毫無關系。也就是說,該模式返回的對象與在構造函數外部超級多對象沒什麽不同。所以構造函數所表示的類型並不是實例的類型。
以上例來說:
colors instanceof SpecialArray;// false
七、 穩妥構造函數模式
1. 代碼示例
function Person(name, age) {
var o = new Object();
//私有變量
var job = ‘developer‘;
o.sayName = function() {
console.log(name);
}
o.sayJob = function() {
console.log(job);
}
return o;
}
var person1 = new Person(‘Bonnie‘, 26);
person.sayName()//‘Bonnie‘
person.sayJob()//‘Job‘
該方式創建的對象,除了sayName、sayJob方法以外,沒有別的辦法訪問變量name、job。
2. 優點
該模式提供了固定的方法來訪問構造函數中的原始數據(構造函數參數、私有變量)。除提供的方法外,沒有其他辦法可以訪問其構造函數中的原始數據。這種安全性使得穩妥構造函數模式非常適合在某些安全執行環境(如ADsafe、Caja)下使用。
參考資料
《JavaScirpt高級程序設計》6.2
JavaScript創建對象的幾種重要模式