這裡的method1每創建一個class1的實例,都會被創建一次,造成了內存的浪費。下一節介紹另一種類定義的機制:prototype對象,可以解決構造函數中定義類成員帶來的缺點。
使用prototype對象定義類成員
上一節介紹了類的實現機制以及構造函數的實現,現在介紹另一種為類添加成員的機制:prototype對象。當new一個function時,該對象的成員將自動賦給所創建的對象,例如:
<script language="Javascript" type="text/Javascript">
<!--
//定義一個只有一個屬性prop的類
function class1(){
this.prop=1;
}
//使用函數的prototype屬性給類定義新成員
class1.prototype.showProp=function(){
alert(this.prop);
}
//創建class1的一個實例
var obj1=new class1();
//調用通過prototype原型對象定義的showProp方法
obj1.showProp();
//-->
</script>
prototype是一個JavaScript對象,可以為prototype對象添加、修改、刪除方法和屬性。從而為一個類添加成員定義。
了解了函數的prototype對象,現在再來看new的執行過程。
(1)創建一個新的對象,並讓this指針指向它;
(2)將函數的prototype對象的所有成員都賦給這個新對象;
(3)執行函數體,對這個對象進行初始化操作;
(4)返回(1)中創建的對象。
和上一節介紹的new的執行過程相比,多了用prototype來初始化對象的過程,這也和prototype的字面意思相符,它是所對應類的實例的原型。這個初始化過程發生在函數體(構造器)執行之前,所以可以在函數體內部調用prototype中定義的屬性和方法,例如:
<script language="Javascript" type="text/Javascript">
<!--
//定義一個只有一個屬性prop的類
function class1(){
this.prop=1;
this.showProp();
}
//使用函數的prototype屬性給類定義新成員
class1.prototype.showProp=function(){
alert(this.prop);
}
//創建class1的一個實例
var obj1=new class1();
//-->
</script>
和上一段代碼相比,這裡在class1的內部調用了prototype中定義的方法showProp,從而在對象的構造過程中就彈出了對話框,顯示prop屬性的值為1。
需要注意,原型對象的定義必須在創建類實例的語句之前,否則它將不會起作用,例如:
<script language="Javascript" type="text/Javascript">
<!--
//定義一個只有一個屬性prop的類
function class1(){
this.prop=1;
this.showProp();
}
//創建class1的一個實例
var obj1=new class1();
//在創建實例的語句之後使用函數的prototype屬性給類定義新成員,只會對後面創建的對象有效
class1.prototype.showProp=function(){
alert(this.prop);
}
//-->
</script>
這段代碼將會產生運行時錯誤,顯示對象沒有showProp方法,就是因為該方法的定義是在實例化一個類的語句之後。
由此可見,prototype對象專用於設計類的成員,它是和一個類緊密相關的,除此之外,prototype還有一個重要的屬性:constructor,表示對該構造函數的引用,例如:
function class1(){
alert(1);
}
class1.prototype.constructor(); //調用類的構造函數
這段代碼運行後將會出現對話框,在上面顯示文字“1”,從而可以看出一個prototype是和一個類的定義緊密相關的。實際上:class1.prototype.constructor===class1。
一種JavaScript類的設計模式
前面已經介紹了如何定義一個類,如何初始化一個類的實例,且類可以在function定義的函數體中添加成員,又可以用prototype定義類的成員,編程的代碼顯得混亂。如何以一種清晰的方式來定義類呢?下面給出了一種類的實現模式。
在JavaScript中,由於對象靈活的性質,在構造函數中也可以為類添加成員,在增加靈活性的同時,也增加了代碼的復雜度。為了提高代碼的可讀性和開發效率,可以采用這種定義成員的方式,而使用prototype對象來替代,這樣function的定義就是類的構造函數,符合傳統意義類的實現:類名和構造函數名是相同的。例如:
function class1(){
//構造函數
}
//成員定義
class1.prototype.someProperty="sample";
class1.prototype.someMethod=function(){
//方法實現代碼
}
雖然上面的代碼對於類的定義已經清晰了很多,但每定義一個屬性或方法,都需要使用一次class1.prototype,不僅代碼體積變大,而且易讀性還不夠。為了進一步改進,可以使用無類型對象的構造方法來指定prototype對象,從而實現類的成員定義:
//定義一個類class1
function class1(){
//構造函數
}
//通過指定prototype對象來實現類的成員定義
class1.prototype={
someProperty:"sample", someMethod:function(){
//方法代碼
},
…//其他屬性和方法.
}
上面的代碼用一種很清晰的方式定義了class1,構造函數直接用類名來實現,而成員使用無類型對象來定義,以列表的方式實現了所有屬性和方法,並且可以在定義的同時初始化屬性的值。這也更象傳統意義面向對象語言中類的實現。只是構造函數和類的成員定義被分為了兩個部分,這可看成JavaScript中定義類的一種固定模式,這樣在使用時會更加容易理解。
注意:在一個類的成員之間互相引用,必須通過this指針來進行,例如在上面例子中的someMethod方法中,如果要使用屬性someProperty,必須通過this.someProperty的形式,因為在JavaScript中每個屬性和方法都是獨立的,它們通過this指針聯系在一個對象上。