讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議86:使用面向對像模擬繼承 >

建議86:使用面向對像模擬繼承

JavaScript是一種弱類型解釋運行的腳本語言,就語言本身來講,它不是一門面向對像語言,但我們可以利用一些語言特性來模擬面向對像編程和繼承機制,這一切都需要從JavaScript中的function講起。

函數是一個定義一次但調用或執行無數次的JavaScript代碼片段。函數可以有零個或多個輸入參數和一個返回值,它通常用於完成一些計算或事務處理等任務。通常可以這樣定義一個function:


function distance(x1,y1,x2,y2){

var dx=x2-x1;

var dy=y2-y1;

return Math.sqrt(dx*dx+dy*dy);

}


在JavaScript中,function不僅是一種語法結構,還可以作為一種數據。這意味著它可以被賦值給變量,在對像或數組中作為元素的屬性存儲,或者作為函數參數傳遞等。例如,把function作為數據使用:


var d=distance;

d(1,1,2,2);


當在一個object中定義和調用一個function時,這個function被稱做該object的一個方法。需要注意的是,當這個function被調用時,這個object會以隱含參數的形式傳入到function中,function內部可以通過this關鍵字來引用這個object的屬性。例如,在下面這個例子的運行結果中,calculator.result的值為2。


var calculator={

operand1:1,

operand2:1,

compute:function{

this.result=this.operand1+this.operand2;

}

};

calculator.compute;

alert(calculator.result);


在JavaScript中,對象的創建通常是通過new運算符來完成的,new關鍵字後面必須是一個可執行的function。例如:


var array=new Array(10);

var today=new Date;


當上面這條創建語句被執行時,首先會創建一個空的對象賦給前面的變量,然後調用後面緊跟的function,並且將這個空的對象作為隱含參數傳入到function內部。這樣在function內部就可以通過this關鍵字引用該對象,做一些對像初始化工作。這樣的function通常被稱為構造方法或簡單構造方法。例如:


function Rectangle(w,h){

this.width=w;

this.height=h;

}

var rect1=new Rectangle(2,4);


到這裡已經有了對象、函數、作為數據的函數、對像方法和構造函數,於是可以使用這些JavaScript的特性模擬寫出類似於Java風格的面向對象的代碼。例如,利用下面代碼就可以模擬面向對象。


function Rectangle(w,h){

this.width=w;

this.height=h;

this.area=function{

return this.width*this.height;

};

}

var rect1=new Rectangle(2,4);

rect1.area;


上面的代碼看起來非常不錯,但似乎還缺少一些東西。面向對象的精髓是對事物的抽像和繼承,以達到代碼重用的目的,在JavaScript中如何做到這一點呢?

在JavaScript中,每一個object都有一個prototype屬性,這個屬性引用了另一個object對象。當需要讀取一個object中的某個屬性p時,JavaScript引擎先查找這個object中是否存在屬性p,如果存在則返回該屬性值,如果不存在則在這個object的prototype對像中查找是否存在屬性p。這樣做會有兩點好處:第一,在每個類中可以抽像出來一些共有的屬性和方法定義在prototype對像中,當使用構造函數創建多個類的實例時,多個實例使用同一prototype對像大大減小了對內存的佔用量。第二,新創建的對象的屬性是在創建對像時由prototype對像帶來的,這樣就可以實現屬性和方法的繼承。

在前面對function的介紹中曾提到,當new操作符被調用時會創建一個空的對象賦給變量並調用後面的構造函數。實際上在利用new操作符創建一個空對像後,還會為這個對象設置它的prototype屬性,這個屬性值等於它的構造函數的prototype屬性值。所有的函數在其定義時就已經自動創建和初始化好了prototype屬性,這個初始化好的prototype屬性指向一個只包含一個constructor屬性的對象,並且這個constructor屬性指向這個function自身。這就解釋了為什麼每一個object都會有一個constructor屬性。運行下面的代碼,將打印出一個名為Object的簡版的函數體。


var obj=new Object;

alert(obj.constructor);


瞭解了JavaScript模擬面向對像編程基礎知識,就可以通過將一些共有的屬性和方法定義在構造方法中來實現屬性的繼承。


function Rectangle(w,h){

this.width=w;

this.height=h;

}

Rectangle.prototype.area=function{

return this.width*this.height;

};


另外,要繼承另一個類並重寫和添加一些方法,可能需要複製這個類的構造方法的prototype對像中的屬性和方法,然後再根據需要重寫和添加一些方法和屬性。