前兩天寫完組合繼承,打算總結一下原型繼承的,不過今天看了一下工廠模式、構造函數模式和原型模式,覺得有必要總結一下以加深印象。
——————————————————————————————————————————————————————————————————————————————————碎碎念。
1.工廠模式
《Javascript 高級程序設計(第3版)》 用了寥寥十多行介紹了工廠模式。我找了一些相關資料,想確定一下這種模式的具體適用場景和優勢。按照資料中的說法,是考慮到 ECMAScript 無法創建類,所以:
創建一個對象,緊接著描述對象的屬性和方法,最后用另一個對象把它們封裝起來當作接口。
按照上面描述的,工廠函數是用來在 Javascript 中實現類似于 Java 中類的功能。通過調用工廠函數,可以創建多個相似的對象實例。
? ? 簡單工廠模式:使用一個類(通常為單體)來生成實例。
? ? 復雜工廠模式:使用子類來決定一個成員變量應該是哪個具體的類的實例。
關于 Javascript 使用工廠模式的優點,根據網上的總結,大概有以下幾點:
a.?消除對象之間的耦合(這個不太明白,消除誰與誰的耦合?);
b. 在進行批次相關設置時,把所有實例化的代碼都集中在一個位置,有助于創建模塊化的代碼,減少代碼量;
c.??用于許多小型對象組成一個大對象。
缺點:
a. 沒有解決對象識別的問題。
?
.....這個模式先留著吧,沒太搞透徹優勢在哪。以后領悟了的話再來補充。引用《Javascript 設計模式與開發實踐》P5中的一段話:
“而在 Javascript 這種類型模糊的語言中,對象多態性是天生的,一個變量既可以指向一個類,又可以隨時指向另外一個類。Javascript 不存在類型耦合問題,自然也沒有必要刻意把對象“延遲”到子類創建,也就是說,Javascript 實際上是不需要工廠方法模式的。 模式的存在首先是能為我們解決什么問題,這種牽強的模擬只會讓人覺得設計模式既難懂又沒什么用。”
2.?構造函數模式
關于封裝: 在 Javascript 中,可以將一些屬性和方法封裝到構造函數內;
關于多態:在 Javascript 中不存在重載的概念,但可以通過參數個數和類型判斷來模擬重載,但是 Javascript 中多態是與生俱來的。一個最簡單的栗子:
同一個構造函數實例化得到的兩個對象實例,可以給它們傳入不同的參數,這兩個對象實例是不同的。這就是面向對象編程的多態。
利用構造函數模式,可以創建多個實例,這些實例都被標定為了特定的類型。構造函數模式相比于工廠模式更為簡單且易于實現,但也存在缺點:
function Person(name,age,job){this.name = name;this.age = age;this.job = job;this.arr = [];this.sayName = function(){alert(this.name);}; } var person1 = new Person("Nicholas",29,"Software Engineer"); var person2 = new Person("Greg",27,"Doctor");console.log(person1.sayName==person2.sayName);//false person1.arr.push(1); console.log(person1.arr);//1 person2.arr.push(2); console.log(person2.arr);//2
利用構造函數每實例化一個實例對象,構造函數內的方法都要在實例上重新創建一次。通過?person1.sayName==person2.sayName 返回的返回結果可以看到兩個實例引用的方法是不同的。
注意:這一點也適用于數組。
可以通過將構造函數的方法放在外部,使得對象實例每次都引用同一個方法。
function Person(name,age,job){this.name = name;this.age = age;this.job = job;this.sayName = sayName; }function sayName(){alert(this.name); }var person1 = new Person("Nicholas",29,"Software Engineer"); var person2 = new Person("Greg",27,"Doctor");console.log(person1.sayName==person2.sayName);//true
通過將構造函數的方法提到外部作為:特權方法。可以使實例獲得相同的方法引用。
但是這種方法也存在一些問題:將方法提到外部作為特權方法使得封裝性變差。如果構造函數內部有很多個方法,這樣做后果更為明顯。
另外再看一個問題:如果構造函數內有多個固定屬性,并且存在多個方法。實例化的時候要為每個對象都復制相同的固定屬性,如果將方法放在構造函數內部,這些方法也要復制,即使都作為特權函數,也存在弊端。
總結,構造函數模式存在兩點不足:1. 浪費內存;2. 可能會使封裝性變差。
3.原型模式
原型模式的出現,解決了構造函數實例化過程中對固定屬性個方法重復深復制的問題。
對于相同的屬性和方法,只要在構造函數的原型對象上申明一次,構造函數的實例就可以共享同一個屬性和方法。
function Person(){}Person.prototype.name = "Nicholas"; Person.prototype.sayName = function(){console.log(this.name); }var person1 = new Person(); var person2 = new Person();console.log(person1.sayName===person2.sayName);//true console.log(person1.name===person2.name);//true
?4.組合使用構造函數模式和原型模式(混合模式)
但是可以看到,雖然原型模式能夠解決固定屬性和方法多次復制的問題,如果屬性要根據不同實例對象改變,這時候最好還是把會隨實例對象改變的屬性放置在構造函數內部,這又用到了構造函數模式。將構造函數模式和原型模式相結合,就能利用這兩個模式的優勢。
function Person(name,age){//會隨實例對象改變的屬性this.name = name;this.age = age; } //不變的屬性或者是方法 Person.prototype.job = "Soft Engineer"; Person.prototype.sayName = function(){console.log(this.name); }var person1 = new Person("Nicholas",28); var person2 = new Person("Shelby",25);
?