類是什么?
許多剛接觸編程的朋友都可能理解不了類,其實類是對我們這個現實世界的模擬,把它說成“類別”或者“類型”可能會更容易理解一些。比如“人”這種動物就是一個類,而具體某一個人就是“人”這個類的一個實例,“人”可以有許多實例(地球人超過六十億了),但“人”這個類只有一個。你或許會說那男人和女人不也是人么?怎么只能有一個?其實這里要談到一個繼承的東西,后邊才講,請繼續看下去。
如何建立一個類?
在C++中是以class來聲明一個類的,JavaScript與C++不同,它使用了與函數一樣的function來聲明,這就讓許多學Jscript 的朋友把類與函數混在一起了,在Jscript中函數與類確實有些混,但使用久了自然而然會理解,這篇文章是針對想進攻面向對象編程的朋友而寫,就不打算一下子討論得太深了。
請看下邊這個類的定義:




上邊的代碼定義了一個WuYouUser(無憂用戶)類,它有個屬性:Name(名字)。Name就是WuYouUser類的一個屬性。
一個類有固定的屬性,但類的實例卻有不同的屬性值,就像我是屬于“人”這個類的,性別是男,而我有一個女同學,她也屬于“人”類,但她的性別屬性值卻為女。
那么如何聲明某個類的一個實例呢?非常簡單:
var Wo = new WuYouUser(); //實例一:“我”
var Biyuan = new WuYouUser(); //實例二:“碧原”(Biyuan哥,不好意思。。。嘿嘿)
類的屬性
這個Wo(我)就是WuYouUser類的一個實例,它擁有WuYouUser給它的一切:Name屬性、Sex屬性以及Age屬性,我們可以這樣子來設置它的屬性:
Wo.Name = "泣紅亭";
很簡單是不是?試著運行
window.document.write(Wo.Name);
看看,是不是輸出了我的名字:泣紅亭?同樣設置一下碧原兄的屬性
Biyuan.Name = "碧原";
運行
window.document.write(Biyuan.Name);
可以看到輸出了"碧原",也就說明了Biyuan與Wo同樣是WuYouUser類的實例,但卻是不同的實體,具有不同的屬性值。
屬性是可以設置默認值的,無憂里都有記錄大家各自發了多少貼子,我們也同樣給WuYouUser類添加一個發貼數量的屬性ArticleCount






一個無憂新用戶剛注冊完之后他的發貼數量為0,在上邊的代碼中可以看到直接給屬性ArticleCount設置值為0。
可以運行一下這樣的代碼:


可以看到輸出了0,說明ArticleCount屬性被我們成功設置默認值為0
類的方法
方法這個詞不大好理解,我覺得說成行為會更容易理解。一個人具有許多共同的行為,比如睡覺、吃飯、走路等等,現在我們給WuYouUser類添加一個發貼的方法。


















既然定義好了這個方法,我們來試試效果如何:
var Wo = new WuYouUser();
Wo.NewArticle();
document.write(Wo.ArticleCount);
可以看到輸出了1,說明我們發貼成功了!真是有歷史紀念意義的一刻,離恐龍等級又近一步了。
靜態屬性
靜態屬性又稱公共屬性,它不屬于某個類的實例,而是直接屬于某個類。比如說無憂用戶有一個屬性:注冊用戶的數量,它是屬于整個無憂用戶的,而不是屬于泣紅亭或者誰的
靜態屬性的聲明方法是:

比如給WuYouUser類定義一個注冊用戶的數量Count:
WuYouUser.prototype.Count = 0;
那么如何讀取它呢?有兩種方法:
1. 直接用 WuYouUser.prototype.Count
2. 使用Wo.Count
這兩者沒有區別,都是得到0
雖然讀取方法可以有兩種,但在改變它的時候卻得特別小心了,請看下邊代碼




你會發現兩者的Count屬性都是1,也就是說WuYouUser.prototype.Count改變了會影響到各個實例的相應屬性,其實原理就是 Wo、Biyuan的Count屬性與WuYouUser.prototype.Count根本就是同一個!
現在來看另外一段代碼:






可以看到如果直接修改實例的靜態屬性值,那么會出現其它實例甚至類的靜態屬性與它不同步了?這是因為直接修改的時候,該實例會生成一個屬于該實例的屬性 Count,這個時候Biyuan.Count不再與WuYouUser.prototype.Count是同一個了,也不與Wo.Count是同一個,這個Count屬性是屬于Biyuan自己所有的,以后改變了它也只是影響它自己而已。
因此如果不是特別的需要,建議不管在讀取還是賦值的時候,都統一使用WuYouUser.prototype.Count這樣的方式,以做到萬無一失!
靜態方法
靜態方法的定義方式是:





與靜態屬性相似,它也有個另稱:公共方法,同樣屬于類本身的。我們現在就來定義一個無憂用戶類的注冊新用戶靜態方法:





現在我們來看看如何用它,同樣有兩種方法:
1.直接使用WuYouUser.prototype.AddOne()
2.使用某實例的AddOne()
這兩種方法沒有什么不同:














可以看出不管是使用Wo.AddOne()還是WuYouUser.prototype.AddOne()效果都是一樣的,都是給WuYouUser.prototype.Count加上1
現在再看一段代碼:












可以看到Wo.Name確實已經變成了"鄭運濤",這個方法似乎是可以用的,但里邊是不是內有天機呢?
再看下邊的代碼,類的定義以及ChangeName的定義我們照樣,但改變一下下邊的代碼:





可以看到我們并沒有定義NewClass.prototype.Name這個靜態屬性,但編譯器給我們自己加了一個。
可是再看下邊輸出Wo.Name,它并不是為"鄭運濤",而是原來的默認值"泣紅亭",說明了什么?
其實很簡單,看一下NewClass的定義里已經有Name這個屬性,因此Wo也有自己的Name屬性,它跟NewClass.prototype.Name并不是同一個的,因此就還是那樣子。
那為什么前一個例子運行了Wo.ChangeName("鄭運濤")卻能夠實現改變Wo.Name屬性呢?其實在這里跟改變Wo.Count的值是同一個道理,編譯器自動給Wo增加了一個方法ChangeName,這個方法代碼與NewClass.prototype.ChangeName一樣,但 Wo.ChangeName是Wo這個實例所特有的,而非NewClass.prototype.ChangeName!那為什么前一個例子運行了Wo.ChangeName("鄭運濤")卻能夠實現改變Wo.Name屬性呢?其實在這里跟改變Wo.Count的值是同一個道理,編譯器自動給Wo增加了一個方法ChangeName,這個方法代碼與NewClass.prototype.ChangeName一樣,但 Wo.ChangeName是Wo這個實例所特有的,而非NewClass.prototype.ChangeName!
分析可知道在靜態方法里盡量不要使用this這樣的關鍵字來引用實例本身的屬性,除非你有特別的目的,而且能夠清楚地明白這里邊的運行機制!
如果真的需要在靜態方法里使用this,可以直接把this當作參數傳進去:





構造函數
一個類在初始化的時候其實也是一個函數的執行過程,這個函數就是構造函數,我們看一下下邊的代碼:






構造函數不需要返回值,但如果你設置了返回值,可以把它當成一個函數來使用。










感覺挺奇妙,對吧?我寫這文章寫著寫著也覺得挺奇妙的,呵呵!
但強烈建議不要把一個類當成一個函數來使用!如果你需要的是一個函數,請直接寫成函數而不要寫成類,以免搞混了。
繼承
繼承的方式就是

先看第一個類的定義:





這個類定義了一個屬性Name,默認值為"泣紅亭"
現在看第二個類的定義:





B.prototype = new A();
運行這一段代碼:
var Obj = new B(); //首先打開警告窗口顯示"泣紅亭",再顯示"男"
可以從上邊的結果看出B類繼承了A類,擁有了A類的屬性Name,并且執行了A類的構造函數,而且A類的構造函數在B類的構造函數執行之前執行。因此我們利用這個可以實現重寫父類的方法以及重設置父類某屬性的默認值:





















結果出現了三次警告窗口,第一個內容為泣紅亭,是執行A類的構造函數里的alert(this.Name),那時候Name屬性值還為"泣紅亭",因為B 類的構造函數還沒執行,第二次內容為"鄭運濤",這是B類里的alert(this.Name),因為B類的構造函數里給Name重賦值為"鄭運濤"。最后是調用了Obj.Show(),執行了不是A類的Show方法里的Show(顯示"這是A類的Show方法"),而是執行了B類的Show(顯示"這是 B類的Show方法"),很明顯Show方法被重寫了。
類作為一個對象時的屬性與方法(不知道如何簡潔地表達,因此用了這么長的題目)
不知道在這里談這個話題是否有點混人耳目,但又覺得不談這篇文章就不算完整,因為文章目的就是要讓人搞清楚類的方方面面。
看了這一小節的題目,或許你會覺得奇怪,類就是類,怎么會“作為一個對象”呢?在JavaScript里,一切都是對象,包括類!對象可以有屬性,可以有方法,類也同樣可以有,但這個非常容易跟前邊說到的靜態屬性與靜態方法搞混了,因此要仔細看清楚兩者的分別!
定義一個類:











從這里可以看出Url這個屬性是WuYouUser自個所有,改變了它與其它類以及它的子類完全無關!
引用類的屬性只有一個辦法,就是類名.屬性名,改變它也一樣。
定義類作為一個對象時的方法:





你或許會覺得奇怪,這里的this是什么?因為ChangeUrl這個方法是屬于對象WuYouUser的,因此this指的就是WuYouUser本身!
可以運行下邊的代碼試試:
document.write(WuYouUser.Url); // http://www.51js.com
WuYouUser.ChangeUrl();
document.write(WuYouUser.Url); // http://51js.com
明顯ChangeUrl直接修改了WuYouUser.Url的值,因此后邊才能輸出http://51js.com
如果你這一節看不明白,也不要著急,編程嘛,許多東東都只能意會不能言傳,而且我又沒口才,說不清楚,只要以后多寫寫代碼,多用用類自然而然會體會到這一些,還有可以去看看JSVM的代碼,里邊幾乎每個類都有用到類作為一個對象時的屬性與方法。