當前位置:才華齋>設計>網頁設計>

面向物件程式設計的javascript基礎

網頁設計 閱讀(1.24W)

我們看到這裡繼承的概念是多麼的直白,“拷貝一個類的prototype 到另外一個類”,好,Code is cheap,看程式碼:

面向物件程式設計的javascript基礎

function class1() { }

function class2() { }

otype = otype;

Property1 = " class 2 additional string " ;

Method1 = function () { alert( " class 2 additional method " ); }

/*

這樣,首先是class2具有了和class1 一樣的prototype,不考慮建構函式,兩個類是等價的。

隨後,又通過prototype給class2賦予了兩個額外的方法。所以class2是在class1的基礎上

增加了屬性和方法,這就實現了類的繼承。

*/

function test() {

var obj = new class2();

// JavaScript提供了instanceof 操作符來判斷一個物件是否是某個類的例項

alert(obj instanceof class2); // true

alert(obj instanceof class1); // ?

}

執行程式碼,結果是不是在我們的意料之中?表面上看,上面的實現完全可行,js也可以正確的理解和實現這種繼承關係,obj同時是class1和 class2的例項,但實質上則不然(我們學習的目的是要知其然更要知其所以然)。js的這種理解實際上是基於一種很簡單的策略,看下面的程式碼,先使用 prototype讓class2 繼承於class1,再在class2 中重複定義method 方法:

// 定義class1

function class1() {

// 建構函式

}

// 定義class1 的成員

otype = {

m1: function () { // 方法1

alert( " class1 method1 " );

}

}

// 定義class2

function class2() {

// 建構函式

}

// 讓class2 繼承於class1

otype = otype;

// 給class2 重複定義方法method

od = function () {

alert( " whose method2? class1 or class2 " );

}

// 建立兩個類的例項

var obj1 = new class1();

var obj2 = new class2();

function test() {

// 分別呼叫兩個物件的method 方法

od();

od();

}

從程式碼執行結果看,method方法在class1,2中執行的結果是相同的。

由此可見,當對class2 進行prototype 的改變時,class1的prototype也隨之改變,即使對class2 的prototype 增減一些成員,class1的成員也隨之改變。所以class1 和class2 僅僅是建構函式不同的兩個類,它們保持著相同的成員定義。說到這裡,相信讀者已經發現了其中的奧妙:class1 和class2 的prototype 是完全相同的',是對同一個物件的引用。其實從這條賦值語句就可以看出來:

//讓class2 繼承於class1

otype=otype;

在js中,除了基本的資料型別(數字、字串、布林型別等),所有的賦值以及函式引數都是引用傳遞,而不是值傳遞。所以上面的語句僅僅是讓class2 的prototype 物件引用class1 的prototype,造成了類成員定義始終保持一致的效果。從這裡也看到了instanceof 操作符的執行機制,它就是判斷一個物件是否是一個prototype 的例項,因為這裡的obj1 和obj2 都是對應於同一個prototype,所以它們instanceof 的結果都是相同的。由此可見,使用prototype 引用拷貝實現繼承不是一種正確的辦法。但在要求不嚴格的情況下,卻也是一種合理的方法,唯一的約束是不允許類成員的覆蓋定義(這裡其實也是js的靈活性的體現)。其實,我們完全可以利用反射機制和prototype 來實現js正確的類繼承:

function class1() {

// 建構函式

}

otype = {

method: function () {

alert( " method1 " );

},

method2: function () {

alert( " method2 " );

}

}

function class2() {

// 建構函式

}

// 讓class2 繼承於class1

for ( var p in otype) {

otype[p] = otype[p]; // 利用反射機制和prototype實現繼承

}

// 覆蓋定義class1中的method 方法

od = function () {

alert( " class2 new method1 " );

}

// 建立兩個類的例項

var obj1 = new class1();

var obj2 = new class2();

function test() {

// 分別呼叫兩個物件的method 方法

od();

od();

// 分別呼叫兩個物件的method2 方法

od2();

od2();

}

從執行結果可見,obj2中重複定義的method 已經覆蓋了繼承的method方法,同時method2 方法未受影響。而且obj1中的method 方法仍然保持了原有的定義。這樣,就實現了正確意義的類的繼承。為了方便開發,可以為每個類新增一個共有的方法,用以實現類的繼承:

// 為類新增靜態方法inherit表示繼承於某類

rit = function (baseClass) {

for ( var p in otype) {

this otype[p] = otype[p];

}

}

function class1() {

// 建構函式

}

otype = {

method: function () {

alert( " method1 " );

},

method2: function () {

alert( " method2 " );

}

}

function class2() {

// 建構函式

}

// 讓class2 繼承於class1

// for (var p in otype) {

// otype[p] = otype[p]; // 利用反射機制和prototype實現繼承

// }

rit(class1); // 等價於上面註釋掉的那一個for迴圈

// 覆蓋定義class1中的method 方法

od = function () {

alert( " class2 new method1 " );

}

// 建立兩個類的例項

var obj1 = new class1();

var obj2 = new class2();

function test() {

// 分別呼叫兩個物件的method 方法

od();

od();

// 分別呼叫兩個物件的method2 方法

od2();

od2();

}

上面的程式碼使邏輯變的更加清楚,也更容易理解。通過這種方法實現的繼承,有一個缺點,就是在class2 中新增類成員定義時,不能給prototype 直接賦值,而只能對其屬性進行賦值,例如不能為:

otype={

//成員定義

}

而只能為:

ertyName=someValue;

odName=function(){

//語句

}

由此可見,這樣實現繼承仍然要以犧牲一定的程式碼可讀性為代價。有沒有“不僅基類可以用物件直接賦值給property,而且在派生類中也可以同樣實現,使程式碼邏輯更加清晰,也更能體現面向物件的語言特點”的js繼承方式?引號裡的說法是多麼的誘人啊,繼續學習去了。