工廠模式
雖然Object構造函數或對象字面量都可以用來創建單個對象,但這些方式有個明顯的缺點:使用同一個接口創建很多對象,會產生大量的重復代碼。為解決這個問題,人們開始使用
工廠模式的一種變體。
function createPerson(name,age,job){ var o=new Object(); o.name=name; o.age=age; o.job=job; o.sayName=function(){ alert(this.name); }; return o; } var person1=createPerson("Nicholas",29,"Software Engineer"); var person2=createPerson("Grey",27,"Doctor");
函數createPerson()能夠根據接受的參數來構建一個包含所有必要信息的Person對象。可以無數次的調用這個函數,而每次它都會返回一個包含三個屬性一個方法的對象。
工廠模式雖然解決了創建多個相似對象的問題,但卻沒有解決對象識別的問題(即怎樣知道一個對象的類型)。
構造函數模式
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.sayName=function(){ alert(this.name); } } var person1=new Person("Nicholas",29,"Software Engineer"); var person2=new Person("Grey",27,"Doctor");
在這個例子中,Person()函數取代了createPerson函數。我們注意到,Person()中的代碼除了與createPerson()中相同的部分外,還存在以下不同之處:
(1):沒有顯式的創建對象;
(2):直接將屬性和方法賦給了this對象;
(3):沒有return語句
原型模式
我們創建的每個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。
function Person(){ } Person.prototype.name="Nicholas"; Person.prototype.age=29; Person.prototype.sayName=function(){ alert(this.name); } var person1=new Person(); person1.sayName(); //"Nicholas" var person2=new Person(); person2.sayName(); //"Nicholas" alert(person1.sayName==person2.sayName); //true
雖然可以通過對象實例訪問保存在原型中的值,但卻不能通過對象實例重寫原型中的值。如果我們在實例中添加一個屬性,而該屬性與實例原型中的一個屬性同名,那我們就在實例
中創建該屬性,該屬性將會屏蔽原型中的屬性。
原型的動態性
由于在原型中查找值的過程是一次搜索,因此我們對原型對象所做的任何個修改都能夠立即從實例上反映出來--即使先創建了實例后個性原型也照樣如此。請看下面的例子:
var friend=new Person(); Person.prototype.sayHi=function(){ alert("HI"); } friend.sayHi(); //"HI" (沒有問題!)
以上代碼先創建了Person的一個實例,并將其保存在person中,然后,下一條語句在Person.prototype中添加了一個方法sayHi().即使person實例是在添加新方法之前創建的,
但它仍然可以訪問這個新方法。
盡管可以隨時為原型添加屬性和方法,并且個性能夠立即在所有對象實例中反映出來,但如果是重寫整個原型對象,那么情況就不一樣了。我們知道,調用構造函數時會為實例
添加一個指向最初原型的[[Prototype]]指針,而把原型修改為另外一個對象就等于切斷了構造函數與最初原型之間的聯系。請記住:實例中的指針僅指向原型,而不指向構造
函數。看下面的例子:
function Person(){ } var friend=new Person(); Person.prototype={ constructor:Person, name:"Nicholas", age:29, job:"Software Engineer", sayName:function(){ alert(this.name); } }; friend.sayName(); //error
動態原型模式
可以通過檢查某個應該存在的方法是否有效,來決定是否需要初始化原型。來看下面一個例子:
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; if(typeof this.sayName!="function"){ Person.prototype.sayName=function(){ alert(this.name); } } } var friend=new Person("Nicholas",29,"Software Engineer"); friend.sayName();
寄生構造函數模式
通常,在前述幾種模式都不適用的情況下,可以使用寄生(parasitic)構造函數模式。這種模式的基本思想是創建一個函數,該函數的作用僅僅是封裝創建對象的代碼,然后再返回
新創建的對象;但從表面上看,這個函數又很像是典型的構造函數。下面是一個例子:
function Person(name,age,job)( var o=new Object(); o.name=name; o.age=age; o.job=job; o.sayName=function(){ alert(this.name); } return o; } var friend=new Person("Nicholas",29,"Software Engineer"); friend.sayName(); //"Nicholas"
在這個例子中,通過在構造函數的末尾添加一個return語句,可以重寫調用構造函數時返回的值。
這個模式可以在特殊情況下用來為對象創建構造函數。假設我們想創建一個具有額外方法的特殊數組。由于不能直接修改Array構造函數,因此可以使用這個模式。
function SpeicalArray(){ var values=new Array(); //創建數組 values.push.apply(values,arguments); //添加值 values.toPipedString=function(){ return this.join("|"); } return values; //返回數組 } var colors=new SpeicalArray("red","blue","green"); alert(colors.toPipedString()); //"red|blue|green"
文章列表