JavaScript 面向對象編程(2) 定義類
更新時間:2010年05月18日 01:15:57 作者:
最近這一段時間事情太多了,沒有時間再繼續(xù)寫,幸好這兩天有點小閑,先小寫一下JavaScript中面向對象一中推薦的方法。
本文承接上一篇JavaScript面向對象編程(1) 基礎。
上篇說過,JavaScript沒有類的概念,需要通過函數來實現類的定義。先通過一個例子說明:
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的定義實際上相當于類的構造函數,最后兩句是創(chuàng)建這個類的實例。先分析第一句:var obj1 = new myClass(); 當用new創(chuàng)建類的實例時,解釋器首先會創(chuàng)建一個空的對象。然后運行這個myClass函數,并將this指針指向這個類的實例。當碰到this.ID = id;和this.Name = name;及this.showMessage = function(){...}時,便會創(chuàng)建這兩個屬性,和這個方法,并把變量id,name的值一級函數的定義賦給這兩個屬性及這個函數對象(shwoMessage)。這個過程相當于初始化這個對象,類似于C# 中的構造函數。最后new返回這個對象。再看第二句:var obj2 = new myClass(); 執(zhí)行過程與上一句代碼相同,即創(chuàng)建一個空對象,然后執(zhí)行myClass這個函數,定義兩個屬性和一個方法。
從上面的分析中可以看到,上面這種實現類的方式,即在函數的定義中定義類的屬性方法。存在著弊端。如果需要創(chuàng)建兩個或更多這個類的實例時,上文是兩個,這些屬性會被重復的創(chuàng)建多次。
那么如何避免這種情況呢?上一篇中也曾提到過用prototype。prototype和它的名字一樣是一個原型,每一個function都有一個子對象prototype,它其實表示這個function對象的成員的集合,由于這里我們使用function實現類的,所以可以說prototype其實就是便是類的成員的集合。prototype定義的屬性和方法執(zhí)行在函數的構造體執(zhí)行之前,所以當new一個對象之前,其實prototype的成員已經執(zhí)行過了。先看一個例子:
function myClass()
{
//構造函數
}
myClass.prototype =
{
ID: 1,
Name: "johnson",
showMessage: function()
{
alert("ID: " + this.ID + ", Name: " + this.Name);
}
}
var obj1 = new myClass();
var obj2 = new myClass();
類的結構還是和前面的例子相同,只不過這里是利用了prototype來實現。還是先看最后兩句,前面說過,prototype是執(zhí)行在函數構造體之前,即執(zhí)行到var obj1 = new myClass();之前,這個類已經有了ID,Name屬性和showMessage方法。執(zhí)行者一句時執(zhí)行過程如下,注意和前一個例子比較:首先還是創(chuàng)建一個空的對象,并把this指針指向這個對象。然后將函數的prototype對象的所有成員都賦給這個對象(注意沒有再創(chuàng)建這些成員)。然后執(zhí)行函數體。最后new返回這個對象。執(zhí)行下一句時:同樣執(zhí)行此過程,不會重復創(chuàng)建這些成員。
上面的代碼還只是一個例子,在實際的項目中,可能出現的是類中有大量的成員,同時可能需要創(chuàng)建大量的實例。這是prototype就會顯示其優(yōu)越性了。另外上面的代碼中使用了大括號語法定義了prototype的成員,這樣看起來代碼更清晰。這是一種比較推薦的類的設計模式。當然在眾多的項目中,可能還會發(fā)現更好的模式,我們也希望能有更優(yōu)化的JavaScript的編程模式不斷推陳出新,也希望隨著時間的推移,各主流瀏覽器也對JavaScript的解析都標準,統(tǒng)一。
上面說過prototype定義的成員是發(fā)生在構造體之前,可以證明一下,在上面的例子中,構造體是空的,在構造函數中加入一句alert(this.Name);,當執(zhí)行到var obj1 = new myClass();時,會看到彈出對話框,顯示正確的屬性值。
寫了這段文字之后承蒙多為兄弟的點評,收獲匪淺。對上面的例子進一步討論,如下代碼:
function subClass(){ }
subClass.prototype =
{
Name: "sub"
}
function myClass()
{
//構造函數
}
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中定義了一個引用類型,其類型是我們自定義的一個subClass類,這個子類中有一個Name屬性。由于prototype對象是共享的,按照我們上面的分析:在執(zhí)行var obj1 = new myClass();時,會把myClass的prototype中的成員復制給這個obj1實例。但這里SubObj是一個引用類型,在執(zhí)行到var obj2 = new myClass();時,prototype中的ID,Name成員會復制到obj2中,但SubObj這個屬性不會復制過去,而是引用了prototype中的SubObj,所以因為上一句修改了obj1.Subobj.Name的值,所以在用new生成obj2實例時,引用到了修改后的值。
所以借用prototype定義類時,依然需要將屬性定義在構造體中,而將方法定義在該構造體的原型上。如下:
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();
關于私有成員,共有成員以及靜態(tài)成員,類的繼承,抽象類,虛方法,類的反射等實現方法,以后還會堅持寫下去。不過我覺得需要說一下的是,我打算寫的是JavaScript面向對象的基礎實現,如果需要深入的學習建議參考李戰(zhàn)老哥的“甘露模型”。
上篇說過,JavaScript沒有類的概念,需要通過函數來實現類的定義。先通過一個例子說明:
復制代碼 代碼如下:
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的定義實際上相當于類的構造函數,最后兩句是創(chuàng)建這個類的實例。先分析第一句:var obj1 = new myClass(); 當用new創(chuàng)建類的實例時,解釋器首先會創(chuàng)建一個空的對象。然后運行這個myClass函數,并將this指針指向這個類的實例。當碰到this.ID = id;和this.Name = name;及this.showMessage = function(){...}時,便會創(chuàng)建這兩個屬性,和這個方法,并把變量id,name的值一級函數的定義賦給這兩個屬性及這個函數對象(shwoMessage)。這個過程相當于初始化這個對象,類似于C# 中的構造函數。最后new返回這個對象。再看第二句:var obj2 = new myClass(); 執(zhí)行過程與上一句代碼相同,即創(chuàng)建一個空對象,然后執(zhí)行myClass這個函數,定義兩個屬性和一個方法。
從上面的分析中可以看到,上面這種實現類的方式,即在函數的定義中定義類的屬性方法。存在著弊端。如果需要創(chuàng)建兩個或更多這個類的實例時,上文是兩個,這些屬性會被重復的創(chuàng)建多次。
那么如何避免這種情況呢?上一篇中也曾提到過用prototype。prototype和它的名字一樣是一個原型,每一個function都有一個子對象prototype,它其實表示這個function對象的成員的集合,由于這里我們使用function實現類的,所以可以說prototype其實就是便是類的成員的集合。prototype定義的屬性和方法執(zhí)行在函數的構造體執(zhí)行之前,所以當new一個對象之前,其實prototype的成員已經執(zhí)行過了。先看一個例子:
復制代碼 代碼如下:
function myClass()
{
//構造函數
}
myClass.prototype =
{
ID: 1,
Name: "johnson",
showMessage: function()
{
alert("ID: " + this.ID + ", Name: " + this.Name);
}
}
var obj1 = new myClass();
var obj2 = new myClass();
類的結構還是和前面的例子相同,只不過這里是利用了prototype來實現。還是先看最后兩句,前面說過,prototype是執(zhí)行在函數構造體之前,即執(zhí)行到var obj1 = new myClass();之前,這個類已經有了ID,Name屬性和showMessage方法。執(zhí)行者一句時執(zhí)行過程如下,注意和前一個例子比較:首先還是創(chuàng)建一個空的對象,并把this指針指向這個對象。然后將函數的prototype對象的所有成員都賦給這個對象(注意沒有再創(chuàng)建這些成員)。然后執(zhí)行函數體。最后new返回這個對象。執(zhí)行下一句時:同樣執(zhí)行此過程,不會重復創(chuàng)建這些成員。
上面的代碼還只是一個例子,在實際的項目中,可能出現的是類中有大量的成員,同時可能需要創(chuàng)建大量的實例。這是prototype就會顯示其優(yōu)越性了。另外上面的代碼中使用了大括號語法定義了prototype的成員,這樣看起來代碼更清晰。這是一種比較推薦的類的設計模式。當然在眾多的項目中,可能還會發(fā)現更好的模式,我們也希望能有更優(yōu)化的JavaScript的編程模式不斷推陳出新,也希望隨著時間的推移,各主流瀏覽器也對JavaScript的解析都標準,統(tǒng)一。
上面說過prototype定義的成員是發(fā)生在構造體之前,可以證明一下,在上面的例子中,構造體是空的,在構造函數中加入一句alert(this.Name);,當執(zhí)行到var obj1 = new myClass();時,會看到彈出對話框,顯示正確的屬性值。
寫了這段文字之后承蒙多為兄弟的點評,收獲匪淺。對上面的例子進一步討論,如下代碼:
復制代碼 代碼如下:
function subClass(){ }
subClass.prototype =
{
Name: "sub"
}
function myClass()
{
//構造函數
}
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中定義了一個引用類型,其類型是我們自定義的一個subClass類,這個子類中有一個Name屬性。由于prototype對象是共享的,按照我們上面的分析:在執(zhí)行var obj1 = new myClass();時,會把myClass的prototype中的成員復制給這個obj1實例。但這里SubObj是一個引用類型,在執(zhí)行到var obj2 = new myClass();時,prototype中的ID,Name成員會復制到obj2中,但SubObj這個屬性不會復制過去,而是引用了prototype中的SubObj,所以因為上一句修改了obj1.Subobj.Name的值,所以在用new生成obj2實例時,引用到了修改后的值。
所以借用prototype定義類時,依然需要將屬性定義在構造體中,而將方法定義在該構造體的原型上。如下:
復制代碼 代碼如下:
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();
關于私有成員,共有成員以及靜態(tài)成員,類的繼承,抽象類,虛方法,類的反射等實現方法,以后還會堅持寫下去。不過我覺得需要說一下的是,我打算寫的是JavaScript面向對象的基礎實現,如果需要深入的學習建議參考李戰(zhàn)老哥的“甘露模型”。
相關文章
javascript 面向對象全新理練之繼承與多態(tài)
前面我們討論了如何在 JavaScript 語言中實現對私有實例成員、公有實例成員、私有靜態(tài)成員、公有靜態(tài)成員和靜態(tài)類的封裝。這次我們來討論一下面向對象程序設計中的另外兩個要素:繼承與多態(tài)。2009-12-12面向對象的Javascript之一(初識Javascript)
Javascript是一門極富表現力的語言,在當今大行其道的Web浪潮中扮演著非常關鍵的作用。合理、高效地利用這門技術,可以讓我們的Web世界多姿多彩。首先,我們認識一下這門技術的幾個獨特的特性2012-01-01