使用Object構造函數或對象字面量
在JavaScript中,我們可以很輕鬆的通過如下方式創建一個對象
var obj1 = new Object();
obj1.proto1 = 'proto1'; // 使用Object構造函數
var obj2 = {
proto2: 'proto2'
} // 使用對象字面量
這兩種方式都能很直觀簡單地幫助我們創建一個新的對象,但這兩種方法也有很明顯的缺點:每一個對象都需要我們不停地給它添加屬性,即使是創建兩個具有相同屬性的對象,這會產生大量的重複代碼。
工廠模式
工廠模式應該是最廣爲人知的一種設計模式了,它抽象了創建對象的具體過程,解決了重複代碼的問題。由於ECMAScript中沒有類的概念,所以只能使用函數來封裝創建對象的細節。如:
function createObj (name, age) {
var obj = new Object();
obj.name = name;
obj.age = age;
return obj
}
var obj1 = createObj('a', 10); // {name: 'a', age: 10}
var obj2 = createObj('b', 11); // {name: 'b', age: 11}
工廠模式雖然解決了重複代碼的問題,但它創建的對象的類型都是Object:
obj1 instanceof createObj // false
obj1 instanceof Object // true
構造函數模式
我們可以使用Array、Object等構造函數來創建我們需要的對象,我們也可以自定義一個構造函數來創建一個自定義類型的對象。構造函數並不是什麼特殊的函數,任何函數使用new關鍵字調用都能作爲構造函數,只是有一個慣例,構造函數的函數名始終以大寫字母開頭。
相比於工廠模式,構造函數的代碼有了幾點不同:
- 沒有顯式地創建的對象(var obj = new Object() 語句)
- 將需要的屬性和方法都賦給了this對象
- 沒有return
上面的代碼可以重寫如下:
function CreateObj (name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name);
}
}
var obj1 = new CreateObj('a', 10); // {name: 'a', age: 10}
var obj2 = new CreateObj('b', 11); // {name: 'b', age: 11}
CreateObj('c', 12); // {name: 'c', age: 12}
obj1 instanceof CreateObj // true
obj1 instanceof Object // true
obj1.sayName == obj2.sayName // false
構造函數讓每個實例都擁有了特定的類型,但這種模式也有缺點:
由於ECMAScript中函數也是對象,所以對於不同的CreateObj實例,它們的sayName屬性就是互不相等但功能完全相同的函數對象,實在沒有必要;況且,既然有了this的存在,sayName屬性完全可以採用下面的寫法:
function CreateObj (name, age) {
this.name = name;
this.age = age;
this.sayName = sayName;
}
function sayName = function () {
console.log(this.name);
}
在全局中創建的sayName函數能夠解決創建多個功能相同的函數的問題,但是它的問題在於只有CreateObj實例能正確使用它,這樣就失去了全局函數的意義,這也在對象只需要一個方法的情況下,如果需要很多方法,就需要創建很多的只能特定對象調用的全局函數,毫無封裝性可言。