AJAX基礎:JavaScript中類別的實作在JavaScript中可以使用function關鍵字來定義一個“類別”,如何為類別新增成員。在函數內透過this指標引用的變數或方法都會成為類別的成員,例如:
以下是程式碼片段:
function class1(){
var s="abc";
this.p1=s;
this.method1=function(){
alert("this is a test method");
}
}
var obj1=new class1();
透過new class1()取得物件obj1,物件obj1便自動取得了屬性p1和方法method1。
在JavaScript中,function本身的定義就是類別的建構函數,結合前面介紹過的物件的性質以及new運算元的用法,以下介紹使用new建立物件的過程。
(1)當解釋器遇到new操作符時便建立一個空物件;
(2)開始運行 class1這個函數,並將其中的this指標都指向這個新建的物件;
(3)因為當給物件不存在的當屬性賦值時,解釋器就會為物件建立該屬性,例如在class1中,當執行到this.p1=s這條語句時,就會加入一個屬性p1,並且把變數s的值賦給它,這樣函數執行就是初始化這個物件的過程,也就是實作建構函數的作用;
(4)當函數執行完後,new操作符就會傳回初始化後的物件。
透過這整個過程,JavaScript中就實現了物件導向的基本機制。由此可見,在JavaScript中,function的定義其實就是實作一個物件的建構器,是透過函數來完成的。這種方式的缺點是:
·將所有的初始化語句、成員定義都放在一起,程式碼邏輯不夠清晰,不易實現複雜的功能。
·每創建一個類別的實例,都要執行一次建構函數。建構函式中定義的屬性和方法總被重複的創建,例如:
以下是程式碼片段:
this.method1=function(){
alert("this is a test method");
}
這裡的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指標連結在一個物件上。