
本文承接上一篇JavaScript面向?qū)ο缶幊蹋?) 基礎(chǔ)。
上篇說(shuō)過(guò),JavaScript沒(méi)有類(lèi)的概念,需要通過(guò)函數(shù)來(lái)實(shí)現(xiàn)類(lèi)的定義。先通過(guò)一個(gè)例子說(shuō)明:
代碼如下:
function myClass()
{
var id = 1;
var name = "johnson";
//properties
this.ID = id;
this.Name = name;
//method
this.showMessage = function()
{
alert("ID: " + this.ID + ", Name: " + this.Name);
}
}
var obj1 = new myClass();
var obj2 = new myClass();
function的定義實(shí)際上相當(dāng)于類(lèi)的構(gòu)造函數(shù),最后兩句是創(chuàng)建這個(gè)類(lèi)的實(shí)例。先分析第一句:var obj1 = new myClass(); 當(dāng)用new創(chuàng)建類(lèi)的實(shí)例時(shí),解釋器首先會(huì)創(chuàng)建一個(gè)空的對(duì)象。然后運(yùn)行這個(gè)myClass函數(shù),并將this指針指向這個(gè)類(lèi)的實(shí)例。當(dāng)碰到this.ID = id;和this.Name = name;及this.showMessage = function(){...}時(shí),便會(huì)創(chuàng)建這兩個(gè)屬性,和這個(gè)方法,并把變量id,name的值一級(jí)函數(shù)的定義賦給這兩個(gè)屬性及這個(gè)函數(shù)對(duì)象(shwoMessage)。這個(gè)過(guò)程相當(dāng)于初始化這個(gè)對(duì)象,類(lèi)似于C# 中的構(gòu)造函數(shù)。最后new返回這個(gè)對(duì)象。再看第二句:var obj2 = new myClass(); 執(zhí)行過(guò)程與上一句代碼相同,即創(chuàng)建一個(gè)空對(duì)象,然后執(zhí)行myClass這個(gè)函數(shù),定義兩個(gè)屬性和一個(gè)方法。
從上面的分析中可以看到,上面這種實(shí)現(xiàn)類(lèi)的方式,即在函數(shù)的定義中定義類(lèi)的屬性方法。存在著弊端。如果需要?jiǎng)?chuàng)建兩個(gè)或更多這個(gè)類(lèi)的實(shí)例時(shí),上文是兩個(gè),這些屬性會(huì)被重復(fù)的創(chuàng)建多次。
那么如何避免這種情況呢?上一篇中也曾提到過(guò)用prototype。prototype和它的名字一樣是一個(gè)原型,每一個(gè)function都有一個(gè)子對(duì)象prototype,它其實(shí)表示這個(gè)function對(duì)象的成員的集合,由于這里我們使用function實(shí)現(xiàn)類(lèi)的,所以可以說(shuō)prototype其實(shí)就是便是類(lèi)的成員的集合。prototype定義的屬性和方法執(zhí)行在函數(shù)的構(gòu)造體執(zhí)行之前,所以當(dāng)new一個(gè)對(duì)象之前,其實(shí)prototype的成員已經(jīng)執(zhí)行過(guò)了。先看一個(gè)例子:
代碼如下:
function myClass()
{
//構(gòu)造函數(shù)
}
myClass.prototype =
{
ID: 1,
Name: "johnson",
showMessage: function()
{
alert("ID: " + this.ID + ", Name: " + this.Name);
}
}
var obj1 = new myClass();
var obj2 = new myClass();
類(lèi)的結(jié)構(gòu)還是和前面的例子相同,只不過(guò)這里是利用了prototype來(lái)實(shí)現(xiàn)。還是先看最后兩句,前面說(shuō)過(guò),prototype是執(zhí)行在函數(shù)構(gòu)造體之前,即執(zhí)行到var obj1 = new myClass();之前,這個(gè)類(lèi)已經(jīng)有了ID,Name屬性和showMessage方法。執(zhí)行者一句時(shí)執(zhí)行過(guò)程如下,注意和前一個(gè)例子比較:首先還是創(chuàng)建一個(gè)空的對(duì)象,并把this指針指向這個(gè)對(duì)象。然后將函數(shù)的prototype對(duì)象的所有成員都賦給這個(gè)對(duì)象(注意沒(méi)有再創(chuàng)建這些成員)。然后執(zhí)行函數(shù)體。最后new返回這個(gè)對(duì)象。執(zhí)行下一句時(shí):同樣執(zhí)行此過(guò)程,不會(huì)重復(fù)創(chuàng)建這些成員。
上面的代碼還只是一個(gè)例子,在實(shí)際的項(xiàng)目中,可能出現(xiàn)的是類(lèi)中有大量的成員,同時(shí)可能需要?jiǎng)?chuàng)建大量的實(shí)例。這是prototype就會(huì)顯示其優(yōu)越性了。另外上面的代碼中使用了大括號(hào)語(yǔ)法定義了prototype的成員,這樣看起來(lái)代碼更清晰。這是一種比較推薦的類(lèi)的設(shè)計(jì)模式。當(dāng)然在眾多的項(xiàng)目中,可能還會(huì)發(fā)現(xiàn)更好的模式,我們也希望能有更優(yōu)化的JavaScript的編程模式不斷推陳出新,也希望隨著時(shí)間的推移,各主流瀏覽器也對(duì)JavaScript的解析都標(biāo)準(zhǔn),統(tǒng)一。
上面說(shuō)過(guò)prototype定義的成員是發(fā)生在構(gòu)造體之前,可以證明一下,在上面的例子中,構(gòu)造體是空的,在構(gòu)造函數(shù)中加入一句alert(this.Name);,當(dāng)執(zhí)行到var obj1 = new myClass();時(shí),會(huì)看到彈出對(duì)話(huà)框,顯示正確的屬性值。
寫(xiě)了這段文字之后承蒙多為兄弟的點(diǎn)評(píng),收獲匪淺。對(duì)上面的例子進(jìn)一步討論,如下代碼:
代碼如下:
function subClass(){ }
subClass.prototype =
{
Name: "sub"
}
function myClass()
{
//構(gòu)造函數(shù)
}
myClass.prototype =
{
ID: 1,
Name: "johnson",
SubObj: new subClass(),
showMessage: function()
{
alert("ID: " + this.ID + ", Name: " + this.Name + "SubObj.Name:" + this.SubObj.Name);
}
}
var obj1 = new myClass();
obj1.SubObj.Name = "XXX";
obj1.showMessage();
var obj2 = new myClass();
obj2.showMessage();
這里在myClass中定義了一個(gè)引用類(lèi)型,其類(lèi)型是我們自定義的一個(gè)subClass類(lèi),這個(gè)子類(lèi)中有一個(gè)Name屬性。由于prototype對(duì)象是共享的,按照我們上面的分析:在執(zhí)行var obj1 = new myClass();時(shí),會(huì)把myClass的prototype中的成員復(fù)制給這個(gè)obj1實(shí)例。但這里SubObj是一個(gè)引用類(lèi)型,在執(zhí)行到var obj2 = new myClass();時(shí),prototype中的ID,Name成員會(huì)復(fù)制到obj2中,但SubObj這個(gè)屬性不會(huì)復(fù)制過(guò)去,而是引用了prototype中的SubObj,所以因?yàn)樯弦痪湫薷牧薿bj1.Subobj.Name的值,所以在用new生成obj2實(shí)例時(shí),引用到了修改后的值。
所以借用prototype定義類(lèi)時(shí),依然需要將屬性定義在構(gòu)造體中,而將方法定義在該構(gòu)造體的原型上。如下:
代碼如下:
function myClass(id, name)
{
this.ID = id;
this.Name = name;
}
myClass.prototype =
{
showMessage: function()
{
alert("ID: " + this.ID + ", Name: " + this.Name);
},
showMessage2: function()
{
alert("Method2");
}
}
var obj1 = new myClass(1, "johnson");
obj1.showMessage();
obj1.Name="John";
obj1.showMessage();
var obj2 = new myClass(2, "Amanda");
obj2.showMessage();
關(guān)于私有成員,共有成員以及靜態(tài)成員,類(lèi)的繼承,抽象類(lèi),虛方法,類(lèi)的反射等實(shí)現(xiàn)方法,以后還會(huì)堅(jiān)持寫(xiě)下去。不過(guò)我覺(jué)得需要說(shuō)一下的是,我打算寫(xiě)的是JavaScript面向?qū)ο蟮幕A(chǔ)實(shí)現(xiàn),如果需要深入的學(xué)習(xí)建議參考李戰(zhàn)老哥的“甘露模型”。
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com