javaScript之建立對象模式
ECMA-262把對象定義為:無序屬性的集合,其屬性可以包含基本值、對象或函數。
對象的每個屬性或方法都有一個名字,而每個名字都映射到一個值,因此可以把ECMAScript的對象想象成散列表,即一組名值對,其中值可以是資料或函數。
每個對象都是基於一個參考型別建立的。
首先先看看早期建立對象的模式:
1、基於Object對象建立對象,是建立自訂對象最簡單方式。
var person=new Object();person.name="singsong";person.age=23;person.info=function(){ alert("name: "+this.name +" "+"age: "+this.age);};
2、用對象字面量建立對象
var person={name:"singsong",age:23,info:function(){ alert("name: "+this.name +" "+"age: "+this.age);}};
雖然Object建構函式或對象字面量都可以用來建立單個對象,但這些方式有個明顯的缺點:使用同一個介面建立很多個物件,會產生大量的重複代碼。解決這問題,可以使用原廠模式的一種變體。
3、原廠模式抽象了建立具體對象的過程,考慮到在ECMAScript中無法建立類,開發人員就發明一種函數,用函數來封裝以特定介面建立對象的細節。
function createPerson(name,age){ var o=new Object(); o.name=name; o.age=age; o.info=function(){ alert("name: "+this.name +" "+"age: "+this.age); } return o;}//產生一個對象。var person=createPerson("singsong",23);
原廠模式雖然解決了建立多個相似對象的問題,但卻沒有解決對象識別的問題即怎麼知道一個對象的類型)。隨著javaScript發展,有一種新模式出現了——建構函式模式
4、建構函式模式
function Person(name, age) { this.name = name; this.age = age; this.info = function() { alert("name: " + this.name + " " + "age: " + this.age); };}var person = new Person("singsong", 23);
建構函式模式與原廠模式的區別:
Person()函數取代了createPerson()函數;
沒有顯式地建立對象即 var o=new Object(););
直接將屬性和方法賦給了this對象;
沒有return語句;
建構函式模式,建立Person執行個體,必須使用new操作符;
注意:函數名Person的首字母是大寫的P,按照慣例,建構函式始終應該以一個大寫字母開頭,而非建構函式則應該以一個小寫字母開頭。這個做法借鑒自其他OO語言,主要是為了區別於ECMAScript中的其他函數,因為建構函式本身也是函數,只不過用來建立對象而已。
以建構函式模式建立的新執行個體會經曆以下4個步驟:
回到正題:那麼建構函式模式怎麼去識別對象的類型?
通過建構函式建立的新執行個體都有一個constructor屬性,該屬性指向Person。
Alert(person.constructor==Person); //true;
對象的constructor屬性最初是用來標識物件類型的。但是,提到檢測物件類型,還是instanceof操作符要更可靠一些。person既是Object的執行個體,同時也是Person的執行個體。
Alert(person instanceof Person); //true;Alert(person instanceof Object); //true;
建立自訂的建構函式意味著將來可以將它的執行個體標識為一種特定的類型,而這正是建構函式模式勝過原廠模式的地方。以這種方式定義的建構函式在定義在Global對象window對象)中的。
建構函式模式雖然好用,但也並非沒有缺點,使用建構函式的主要問題,就是在每一個方法都要在每個勢執行個體上建立一遍,
5、原型模式
建立的每個函數都有一個prototype原型)屬性,這個屬性是一個指標,指向一個對象,而這個對象的用途是包含可以有特定類型的所有執行個體類型共用的屬性和方法。
function Person() {}Person.prototype.name = "singsong";Person.prototype.age = 23;Person.prototype.info = function() { alert("name: " + this.name + " " + "age: " + this.age);};var personOne = new Person();var personTwo= new Person();personOne.name="Tom";alert(personOne.name);//Tom 來自執行個體alert(personTwo.name);//singsong 來自原型
可用hasOwnPrototype方法可以檢測一個屬性是存在於執行個體中,還是存在於原型中。由於這個方法是從Object對象繼承過來的,只在給定屬性存在於對象執行個體時,才會返回true。
alert(personOne.hasOwnProperty("name"));//來自執行個體 true alert(personTwo.hasOwnProperty("name"));//來自原型 falsedelete personOne.name ;//刪除personFive中name屬性alert(personTwo.hasOwnProperty("name"));// false
原型模式缺點:它省略了為建構函式傳遞初始化參數這一環節,結果所有執行個體在預設情況下都將取得相同的屬性值。原型中所有屬性是被很多執行個體共用的,這種對於函數非常適合,對那些包含基本值的屬性到也說得過去。可以通過在執行個體上添加一個同名屬性,可以隱藏原型中的對應屬性。然而,對於包含參考型別值的屬性,它是指向同一個指標的。
6、組合使用建構函式模式和原型模式
此組合模式是建立自訂類型最常見方式,建構函式模式用於定義執行個體屬性,而原型模式用於定義方法和共用的屬性。
function Person(name, age) { this.name = name; this.age = age; } Person.prototype = { constructor: Person, info: function() { alert("name: " + this.name + " " + "age: " + this.age); } } var person = new Person("singsong", 23); person.info();}
這種建構函式與原型混成的模式,是目前在ECMAScript中使用最廣泛,認同度最高的一種建立自訂類型的方法。可以說,這是用來定義參考型別的預設的一種預設模式。
7、動態原型模式
動態原型模式把所有資訊都封裝在建構函式中。通過在建構函式中初始化原型,有保持了同時使用建構函式和原型的優點。
function Person(name, age) { this.name = name; this.age = age; if (typeof this.info != "function") { Person.prototype.info = function() { alert("name: " + this.name + " " + "age: " + this.age); } };}var person = new Person("singsong", 23);person.info();
使用動態原型模式時,不能使用對象字面量重寫原型,如果在已經建立了執行個體的情況下重寫原型,那麼就會切斷現有執行個體與新原型之間的聯絡。
8、寄生建構函式模式
這種模式的基本思想是建立一個函數,該函數的作用僅僅是封裝建立對象的代碼,然後返回新建立的對象。
function Person(name, age) { var o = new Object(); o.name = name; o.age = age; o.info = function() { alert("name: " + this.name + " " + "age: " + this.age); }; return o; } var person = new Person("singsong", 23); person.info();
除了使用new操作符並把使用的封裝函數叫做建構函式之外,這個模式跟原廠模式其實是一模一樣。建構函式在不傳回值得情況下,預設會返回新對象執行個體。而通過在建構函式的末尾添加一個return語句,可以重寫調用建構函式時返回的值。
這個模式可以在特殊的情況下用來為對象建立建構函式。如由於不能直接修改原生對象的建構函式,可以藉助此模式,重建立建構函式來添加額外方法。
注意:寄生建構函式模式返回的對象與建構函式或者與建構函式的原型屬性之間沒有關係;也就是說,建構函式返回的對象與在建構函式外部建立的對象沒有什麼不同。因此,就不能依賴instanceof操作符來確定物件類型了。由於存在此上述問題,建議在可以使用其他模式的情況下,不要使用這種模式。
9、 穩妥建構函式模式
Douglas Crockford發明了javaScript中的穩妥對象durable objects)這個概念。
所謂穩妥對象就是指沒有公用屬性,而且其方法也不引用this的對象。穩妥對象最適合在一些安全的環境中這些環境中會禁止使用this 和 new),或者在防止資料被其他應用程式改動時使用。
function Person(name, age) { var o = new Object(); o.info = function() { alert("name: " + name + " " + "age: " + age); }; return o; } var person = Person("singsong", 23); person.info();
與寄生建構函式模式類似,使用穩妥建構函式模式建立的對象魚建構函式之間也沒有什麼關係,因此instanceof操作符對這種對象也沒有意義。
本文出自 “singsong” 部落格,請務必保留此出處http://singsong.blog.51cto.com/2982804/1287812