先透過一個範例來說明:
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的定義其實相當於類別的建構函數,最後兩句是建立這個類別的實例。先分析第一句:var obj1 = new myClass(); 當用new建立類別的實例時,解釋器會先建立一個空的物件。然後執行這個myClass函數,並將this指標指向這個類別的實例。當碰到this.ID = id;和this.Name = name;及this.showMessage = function(){...}時,便會創建這兩個屬性,和這個方法,並把變量id,name的值一級函數的定義賦給這兩個屬性及這個函數物件(shwoMessage)。這個過程相當於初始化這個對象,類似C# 中的建構子。最後new回傳這個物件。再看第二句:var obj2 = new myClass(); 執行過程與上一句程式碼相同,即建立一個空對象,然後執行myClass這個函數,定義兩個屬性和一個方法。
從上面的分析可以看到,上面這種實作類別的方式,也就是在函數的定義中定義類別的屬性方法。存在著弊端。如果需要建立兩個或更多這個類別的實例時,上文是兩個,這些屬性會被重複的創建多次。
那麼要如何避免這種情況呢? prototype和它的名字一樣是一個原型,每一個function都有一個子物件prototype,它其實表示這個function物件的成員的集合,由於這裡我們使用function實作類別的,所以可以說prototype其實就是便是類的成員的集合。 prototype定義的屬性和方法執行在函式的建構體執行之前,所以當new一個物件之前,其實prototype的成員已經執行過了。先看一個例子:
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是執行在函數構造體之前,即執行到var obj1 = new myClass();之前,這個類別已經有了ID,Name屬性和showMessage方法。執行者一句時執行過程如下,注意和前一個例子比較:首先還是創建一個空的對象,並把this指針指向這個對象。然後將函數的prototype物件的所有成員都賦給這個物件(注意沒有再建立這些成員)。然後執行函數體。最後new回傳這個物件。執行下一句時:同樣執行此過程,不會重複建立這些成員。
上面的程式碼還只是一個例子,在實際的專案中,可能出現的是類別中有大量的成員,同時可能需要建立大量的實例。這是prototype就會顯示其優越性了。另外上面的程式碼使用了大括號語法定義了prototype的成員,這樣看起來程式碼比較清楚。這是一種比較推薦的類別的設計模式。當然在眾多的專案中,可能還會發現更好的模式,我們也希望能有更優化的JavaScript的程式模式不斷推陳出新,也希望隨著時間的推移,各主流瀏覽器也對JavaScript的解析都標準,統一。
上面說過prototype定義的成員是發生在構造體之前,可以證明一下,在上面的例子中,構造體是空的,在構造函數中加入一句alert(this.Name);,當執行到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物件是共享的,按照我們上面的分析:在執行var obj1 = new myClass();時,會把myClass的prototype中的成員複製給這個obj1實例。但這裡SubObj是一個引用類型,在執行到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();
以上是JavaScript 透過函數來實作定義類別實例用法詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!