[轉載]javascript 的面向對象特性參考

 

轉載地址:http://www.79cn.com/tbbs/main/dispbbs.asp?boardID=11&RootID=778&ID=784

最近在看用javascript+css實現rich client。javascript 也是一個蠻有意思的語言。特別是其面向對象的實現和其他“標準”的OO launguage有很大的不同。但是,都是動態語言,我還是覺得它比起python語法和庫都差得太遠。可是沒有explorer支持python開發啊。。。:(

這是我學習javascript中面向對象特性的一點總結。希望對具有其他語言的面向對象設計經驗的朋友理解javascript的OO有所幫助。我具有c++,java和python的面向對象設計的經驗。

總的感受, javascript作爲一種弱類型的動態語言,語法接近於java,但其面向對象的方式更和python相識。

1 面向對象的特性

類,成員變量,成員函數,類變量,類方法,繼承,多態

1) 類

類的定義:function Circle(r) { this.r = r; }

類的實例化: c = Circle(3);

2)成員變量

成員變量在初始化函數裏申明:this.r = r;

注意,在對象生成後,你也可以給它附上成員變量,比如c.name="my circle",

但是除非特別的需要,我強烈建議你不要這樣做。也就是所有的成員都應在初始化函數裏聲明。我認爲這是一種好的style。

這一點和python很相識。

3)成員函數

成員函數的標準形式是這樣的:

Cricle.prototype.area = function() { return 3.14 * this.r * this.r; }

這和java或python或c++都大不一樣。但爲了幫助理解,你可以把prototype看作基類。

prototype裏面的變量或方法,是所有對象共享的。

比如,c.area()調用最終就會讓解釋器調用到Circle.prototype.area().

相比於java和c++,javascript具有他們都沒有的一個語義,也就是你可以在prototype裏定義變量。定義在prototype裏的變量可以被所有的實例共享量。所以一般它應該是一個常數,比如:Circle.prototype.PI = 3.14.

顯然,prototype裏的變量和方法都應該是不變的。每一個對象實例都不應該取修改prototype中的內容。雖然語言允許你可以這樣做,但這樣做沒有任何意義,也違反了面向對象的語義(想想,java會讓你動態修改一個類的方法嗎)。

當然,對於多態是另外一回事,在後面詳述。

而且,我建議所有的成員函數都在緊接類定義的地方定義。而不應該在代碼運行的某個地方對一個對象實例增加/修改成員函數。這樣的結果是javascript的類定義儘量向java看齊。使得代碼更清晰。

4)類變量

類變量是屬於一個類的變量。就像java裏用static修飾的變量。因爲它屬於類,所以它也應該是一個常量。實例不應該去修改它,雖然你可以(java裏可以用final修飾,使得類變量一旦定義,就不能修改)。這裏可以看到,類變量和prototype裏定義的變量的功能是相似的。確實如此,他們的目的都是一樣的。但他們的訪問方式

不一樣。比如:

Circle.prototype.PI = 3.14;

Circle.PI = 3.14;

//用prototype裏的變量

Circle.prototype.area1 = function() { return this.PI * this.r * this.r; }

//用類變量

Circle.prototype.area2 = function() { return Circle.PI * this.r * this.r; }

5)類方法

這個概念應該很簡單。注意類方法裏絕對不要用this關鍵字,和java完全一樣。

Circle.max = function(a, b) {
    return a.r > b.r ? a : b;
}

theMax = Circle(new Circle(1), new Circle(4));

6)繼承

子類繼承父類,那麼 “子類實例” 具有和 “父類實例” 完全一樣的行爲。javascript是這樣實現的。

function SubCircle(x, y, r) {
  this.x = x;
  this.y = y;
  this.r =r;
}

SubCircle.prototype = new Circle(0);
記得前面說的嗎?可以把prototype看作一個基類。這裏,prototype確確實實是一個基類。它是如何實現的呢?

舉例如下:
sc = SubCirlce(1,1,3);
sc.area();

調用的傳遞:
sc.area()->sc.prototype.area()->Circle(0).area()->Circle.prototype.area().
看來是不是很奇妙呢。

通過這種方式,javascript實現了繼承。

7)多態

多態是子類會定義和父類具有相同signature的方法。假設在SubCircle所在的空間PI=100,而面積公式也變爲 PI*R*R*R。

SubCircle.prototype.PI = 100

SubCircle.prototype.area = function() {
   return this.PI*this.r*this.r*this.r;
}
Sc.area()

這樣的操作可以認爲是:

Sc.PI->sc.prototype.PI->Cricle(0).PI = 100

Sc.area()->sc.prototype.area()->Circle(0).area.
這個時候,調用過程是這樣的

sc.area()->sc.prototype.area(),在這裏解釋器發現了area這個方法,於是它就調用此方法。

而Cricle.prototype.area就永遠也不會被調用。PI的調用也是如此。那麼子類如何想調用父類的方法應怎麼辦呢?好像沒有什麼辦法哦,誰知道可以告訴我。但面向對象的理論告訴我們,繼承主要是提供接口而不是代碼複用,所以還是少有這樣的念頭爲好 :)。

下面是一個例子程序。包含上面的所有的概念。
例子
///////////define: Cricle//////////////////
function Circle(r) {
this.r = r;
}
Circle.PI = 3.14;
Circle.prototype.PI = 3.14;
Circle.prototype.area = function() { return Circle.PI*this.r*this.r; }
Circle.prototype.area2 = function() { return this.PI*this.r*this.r; }


//// test
c = new Circle(3);
//alert("area1 :"+c.area());
//alert("area2 :"+c.area2());

Circle.max = function(a, b) { return a.r>b.r ? a.r : b.r; }
//alert("max is "+Circle.max(new Circle(1), new Circle(3)));

c1 = new Circle(1);
c2 = new Circle(1);
c2.PI = 100;//Circle.prototype.PI=100;

//alert("c1.area1 "+c1.area());
//alert("c1.area2 "+c1.area2());
//alert("c2.area1 "+c2.area());
//alert("c2.area2 "+c2.area2());

////////////////////////define: SubCircle //////////////////
function SubCircle(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
}
SubCircle.prototype = new Circle(0);
SubCircle.prototype.PI = 100;
SubCircle.prototype.move2 = function(x, y) { this.x = x; this.y = y;}
SubCircle.prototype.area = function() { return this.PI*this.r*this.r*this.r; }

//// test
sc = new SubCircle(0,0,2);

alert(sc.area());

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章