javascript 面向?qū)ο笕吕砭氈屠^承
更新時(shí)間:2009年12月03日 19:22:16 作者:
利用原型繼承的關(guān)鍵有兩步操作,需要的朋友可以參考下。
首先創(chuàng)建一個(gè)父類的實(shí)例化對象,然后將該對象賦給子類的 prototype 屬性。
這樣,父類中的所有公有實(shí)例成員都會被子類繼承。并且用 instanceof 運(yùn)算符判斷時(shí),子類的實(shí)例化對象既屬于子類,也屬于父類。
然后將子類本身賦值給它的 prototype 的 constructor 屬性。(注意:這里賦值的時(shí)候是沒有 () 的?。?
這一步是為了保證在查看子類的實(shí)例化對象的 constructor 屬性時(shí),看到的是子類的定義,而不是其父類的定義。
接下來,通過對 o.method1() 調(diào)用的結(jié)果我們會看到,子類繼承來的公有實(shí)例方法中,如果調(diào)用了私有實(shí)例字段或者私有實(shí)例方法,則所調(diào)用的這些私有實(shí)例成員是屬于父類的。
同樣,通過對 o.method2() 調(diào)用的結(jié)果我們看到,子類中定義的實(shí)例方法,如果調(diào)用了私有實(shí)例字段或者私有實(shí)例方法,則所調(diào)用的這些私有實(shí)例成員是屬于子類的。
通過對 o.method() 調(diào)用的結(jié)果我們看到,定義在父類原型上的方法,會被子類繼承。
通過對 o.method3() 調(diào)用的結(jié)果我們看到,子類中定義的實(shí)例方法是不能訪問父類中定義的私有實(shí)例成員的。
最后,通過對 subClass.staticMethod() 調(diào)用的結(jié)果我們看到,靜態(tài)成員是不會被繼承的。
2.4 調(diào)用繼承法
調(diào)用繼承的本質(zhì)是,在子類的構(gòu)造器中,讓父類的構(gòu)造器方法在子類的執(zhí)行上下文上執(zhí)行,父類構(gòu)造器方法上所有通過 this 方式操作的內(nèi)容實(shí)際上都都是操作的子類的實(shí)例化對象上的內(nèi)容。因此,這種做法僅僅為了減少重復(fù)代碼的編寫。
function parentClass() {
// private field
var x = "I'm a parentClass field!";
// private method
function method1() {
alert(x);
alert("I'm a parentClass method!");
}
// public field
this.x = "I'm a parentClass object field!";
// public method
this.method1 = function() {
alert(x);
alert(this.x);
method1();
}
}
parentClass.prototype.method = function () {
alert("I'm a parentClass prototype method!");
}
parentClass.staticMethod = function () {
alert("I'm a parentClass static method!");
}
function subClass() {
// inherit
parentClass.call(this);
// private field
var x = "I'm a subClass field!";
// private method
function method2() {
alert(x);
alert("I'm a subClass method!");
}
// public field
this.x = "I'm a subClass object field!";
// public method
this.method2 = function() {
alert(x);
alert(this.x);
method2();
}
this.method3 = function() {
method1();
}
}
// test
var o = new subClass();
alert(o instanceof parentClass); // false
alert(o instanceof subClass); // true
alert(o.constructor); // function subClass() {...}
o.method1(); // I'm a parentClass field!
// I'm a subClass object field!
// I'm a parentClass field!
// I'm a parentClass method!
o.method2(); // I'm a subClass field!
// I'm a subClass object field!
// I'm a subClass field!
// I'm a subClass method!
o.method(); // Error!!!
o.method3(); // Error!!!
subClass.staticMethod(); // Error!!!
上面這個(gè)例子很好的反映出了如何利用調(diào)用繼承法來實(shí)現(xiàn)繼承。
利用調(diào)用繼承的關(guān)鍵只有一步操作:
就是在子類定義時(shí),通過父類的 call 方法,將子類的 this 指針傳入。使父類方法在子類上下文中執(zhí)行。
這樣,父類中的所有在父類內(nèi)部通過 this 方式定義的公有實(shí)例成員都會被子類繼承。
用 instanceof 運(yùn)算符判斷時(shí),子類的實(shí)例化對象只屬于子類,不屬于父類。
查看子類的實(shí)例化對象的 constructor 屬性時(shí),看到的是子類的定義,不是其父類的定義。
接下來,通過對 o.method1() 和 o.method2() 調(diào)用的結(jié)果跟原型繼承法的調(diào)用結(jié)果是相同的,所說明的問題也是一樣的,這里不再重復(fù)。
通過對 o.method() 調(diào)用的結(jié)果我們看到,定義在父類原型上的方法,不會被子類繼承。
通過對 o.method3() 調(diào)用的結(jié)果我們看到,子類中定義的實(shí)例方法同樣不能訪問父類中定義的私有實(shí)例成員的。
最后,通過對 subClass.staticMethod() 調(diào)用的結(jié)果我們看到,靜態(tài)成員同樣不會被繼承的。
最后,還有一點(diǎn),在這個(gè)例子中沒有體現(xiàn)出來,就是通過調(diào)用繼承法,可以實(shí)現(xiàn)多繼承。也就是說,一個(gè)子類可以從多個(gè)父類中繼承通過 this 方式定義在父類內(nèi)部的所有公有實(shí)例成員。
作為一種弱類型語言,javascript 提供了豐富的多態(tài)性,javascript 的多態(tài)性是其它強(qiáng)類型面向?qū)ο笳Z言所不能比的。
多態(tài)
重載和覆蓋
先來說明一下重載和覆蓋的區(qū)別。重載的英文是 overload,覆蓋的英文是 override。發(fā)現(xiàn)網(wǎng)上大多數(shù)人把 override 當(dāng)成了重載,這個(gè)是不對的。重載和覆蓋是有區(qū)別的。
重載的意思是,同一個(gè)名字的函數(shù)(注意這里包括函數(shù))或方法可以有多個(gè)實(shí)現(xiàn),他們依靠參數(shù)的類型和(或)參數(shù)的個(gè)數(shù)來區(qū)分識別。
而覆蓋的意思是,子類中可以定義與父類中同名,并且參數(shù)類型和個(gè)數(shù)也相同的方法,這些方法的定義后,在子類的實(shí)例化對象中,父類中繼承的這些同名方法將被隱藏。
重載
javascript 中函數(shù)的參數(shù)是沒有類型的,并且參數(shù)個(gè)數(shù)也是任意的,例如,盡管你可以定義一個(gè):
function add(a, b) {
return a + b;
}
這樣的函數(shù),但是你仍然可以再調(diào)用它是帶入任意多個(gè)參數(shù),當(dāng)然,參數(shù)類型也是任意的。至于是否出錯(cuò),那是這個(gè)函數(shù)中所執(zhí)行的內(nèi)容來決定的,javascript 并不根據(jù)你指定的參數(shù)個(gè)數(shù)和參數(shù)類型來判斷你調(diào)用的是哪個(gè)函數(shù)。
因此,要定義重載方法,就不能像強(qiáng)類型語言中那樣做了。但是你仍然可以實(shí)現(xiàn)重載。就是通過函數(shù)的 arguments 屬性。例如:
function add() {
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}
這樣你就實(shí)現(xiàn)了任意多個(gè)參數(shù)加法函數(shù)的重載了。
當(dāng)然,你還可以在函數(shù)中通過 instanceof 或者 constructor 來判斷每個(gè)參數(shù)的類型,來決定后面執(zhí)行什么操作,實(shí)現(xiàn)更為復(fù)雜的函數(shù)或方法重載??傊?,javascript 的重載,是在函數(shù)中由用戶自己通過操作 arguments 這個(gè)屬性來實(shí)現(xiàn)的。
覆蓋
實(shí)現(xiàn)覆蓋也很容易,例如:
function parentClass() {
this.method = function() {
alert("parentClass method");
}
}
function subClass() {
this.method = function() {
alert("subClass method");
}
}
subClass.prototype = new parentClass();
subClass.prototype.constructor = subClass;
var o = new subClass();
o.method();
這樣,子類中定義的 method 就覆蓋了從父類中繼承來的 method 方法了。
你可能會說,這樣子覆蓋是不錯(cuò),但 java 中,覆蓋的方法里面可以調(diào)用被覆蓋的方法(父類的方法),在這里怎么實(shí)現(xiàn)呢?也很容易,而且比 java 中還要靈活,java 中限制,你只能在覆蓋被覆蓋方法的方法中才能使用 super 來調(diào)用次被覆蓋的方法。我們不但可以實(shí)現(xiàn)這點(diǎn),而且還可以讓子類中所有的方法中都可以調(diào)用父類中被覆蓋的方法。看下面的例子:
function parentClass() {
this.method = function() {
alert("parentClass method");
}
}
function subClass() {
var method = this.method;
this.method = function() {
method.call(this);
alert("subClass method");
}
}
subClass.prototype = new parentClass();
subClass.prototype.constructor = subClass;
var o = new subClass();
o.method();
你會發(fā)現(xiàn),原來這么簡單,只要在定義覆蓋方法前,定義一個(gè)私有變量,然后把父類中定義的將要被覆蓋的方法賦給它,然后我們就可以在后面繼續(xù)調(diào)用它了,而且這個(gè)是這個(gè)方法是私有的,對于子類的對象是不可見的。這樣跟其它高級語言實(shí)現(xiàn)的覆蓋就一致了。
最后需要注意,我們在覆蓋方法中調(diào)用這個(gè)方法時(shí),需要用 call 方法來改變執(zhí)行上下文為 this(雖然在這個(gè)例子中沒有必要),如果直接調(diào)用這個(gè)方法,執(zhí)行上下文就會變成全局對象了。
這樣,父類中的所有公有實(shí)例成員都會被子類繼承。并且用 instanceof 運(yùn)算符判斷時(shí),子類的實(shí)例化對象既屬于子類,也屬于父類。
然后將子類本身賦值給它的 prototype 的 constructor 屬性。(注意:這里賦值的時(shí)候是沒有 () 的?。?
這一步是為了保證在查看子類的實(shí)例化對象的 constructor 屬性時(shí),看到的是子類的定義,而不是其父類的定義。
接下來,通過對 o.method1() 調(diào)用的結(jié)果我們會看到,子類繼承來的公有實(shí)例方法中,如果調(diào)用了私有實(shí)例字段或者私有實(shí)例方法,則所調(diào)用的這些私有實(shí)例成員是屬于父類的。
同樣,通過對 o.method2() 調(diào)用的結(jié)果我們看到,子類中定義的實(shí)例方法,如果調(diào)用了私有實(shí)例字段或者私有實(shí)例方法,則所調(diào)用的這些私有實(shí)例成員是屬于子類的。
通過對 o.method() 調(diào)用的結(jié)果我們看到,定義在父類原型上的方法,會被子類繼承。
通過對 o.method3() 調(diào)用的結(jié)果我們看到,子類中定義的實(shí)例方法是不能訪問父類中定義的私有實(shí)例成員的。
最后,通過對 subClass.staticMethod() 調(diào)用的結(jié)果我們看到,靜態(tài)成員是不會被繼承的。
2.4 調(diào)用繼承法
調(diào)用繼承的本質(zhì)是,在子類的構(gòu)造器中,讓父類的構(gòu)造器方法在子類的執(zhí)行上下文上執(zhí)行,父類構(gòu)造器方法上所有通過 this 方式操作的內(nèi)容實(shí)際上都都是操作的子類的實(shí)例化對象上的內(nèi)容。因此,這種做法僅僅為了減少重復(fù)代碼的編寫。
復(fù)制代碼 代碼如下:
function parentClass() {
// private field
var x = "I'm a parentClass field!";
// private method
function method1() {
alert(x);
alert("I'm a parentClass method!");
}
// public field
this.x = "I'm a parentClass object field!";
// public method
this.method1 = function() {
alert(x);
alert(this.x);
method1();
}
}
parentClass.prototype.method = function () {
alert("I'm a parentClass prototype method!");
}
parentClass.staticMethod = function () {
alert("I'm a parentClass static method!");
}
function subClass() {
// inherit
parentClass.call(this);
// private field
var x = "I'm a subClass field!";
// private method
function method2() {
alert(x);
alert("I'm a subClass method!");
}
// public field
this.x = "I'm a subClass object field!";
// public method
this.method2 = function() {
alert(x);
alert(this.x);
method2();
}
this.method3 = function() {
method1();
}
}
// test
var o = new subClass();
alert(o instanceof parentClass); // false
alert(o instanceof subClass); // true
alert(o.constructor); // function subClass() {...}
o.method1(); // I'm a parentClass field!
// I'm a subClass object field!
// I'm a parentClass field!
// I'm a parentClass method!
o.method2(); // I'm a subClass field!
// I'm a subClass object field!
// I'm a subClass field!
// I'm a subClass method!
o.method(); // Error!!!
o.method3(); // Error!!!
subClass.staticMethod(); // Error!!!
上面這個(gè)例子很好的反映出了如何利用調(diào)用繼承法來實(shí)現(xiàn)繼承。
利用調(diào)用繼承的關(guān)鍵只有一步操作:
就是在子類定義時(shí),通過父類的 call 方法,將子類的 this 指針傳入。使父類方法在子類上下文中執(zhí)行。
這樣,父類中的所有在父類內(nèi)部通過 this 方式定義的公有實(shí)例成員都會被子類繼承。
用 instanceof 運(yùn)算符判斷時(shí),子類的實(shí)例化對象只屬于子類,不屬于父類。
查看子類的實(shí)例化對象的 constructor 屬性時(shí),看到的是子類的定義,不是其父類的定義。
接下來,通過對 o.method1() 和 o.method2() 調(diào)用的結(jié)果跟原型繼承法的調(diào)用結(jié)果是相同的,所說明的問題也是一樣的,這里不再重復(fù)。
通過對 o.method() 調(diào)用的結(jié)果我們看到,定義在父類原型上的方法,不會被子類繼承。
通過對 o.method3() 調(diào)用的結(jié)果我們看到,子類中定義的實(shí)例方法同樣不能訪問父類中定義的私有實(shí)例成員的。
最后,通過對 subClass.staticMethod() 調(diào)用的結(jié)果我們看到,靜態(tài)成員同樣不會被繼承的。
最后,還有一點(diǎn),在這個(gè)例子中沒有體現(xiàn)出來,就是通過調(diào)用繼承法,可以實(shí)現(xiàn)多繼承。也就是說,一個(gè)子類可以從多個(gè)父類中繼承通過 this 方式定義在父類內(nèi)部的所有公有實(shí)例成員。
作為一種弱類型語言,javascript 提供了豐富的多態(tài)性,javascript 的多態(tài)性是其它強(qiáng)類型面向?qū)ο笳Z言所不能比的。
多態(tài)
重載和覆蓋
先來說明一下重載和覆蓋的區(qū)別。重載的英文是 overload,覆蓋的英文是 override。發(fā)現(xiàn)網(wǎng)上大多數(shù)人把 override 當(dāng)成了重載,這個(gè)是不對的。重載和覆蓋是有區(qū)別的。
重載的意思是,同一個(gè)名字的函數(shù)(注意這里包括函數(shù))或方法可以有多個(gè)實(shí)現(xiàn),他們依靠參數(shù)的類型和(或)參數(shù)的個(gè)數(shù)來區(qū)分識別。
而覆蓋的意思是,子類中可以定義與父類中同名,并且參數(shù)類型和個(gè)數(shù)也相同的方法,這些方法的定義后,在子類的實(shí)例化對象中,父類中繼承的這些同名方法將被隱藏。
重載
javascript 中函數(shù)的參數(shù)是沒有類型的,并且參數(shù)個(gè)數(shù)也是任意的,例如,盡管你可以定義一個(gè):
復(fù)制代碼 代碼如下:
function add(a, b) {
return a + b;
}
這樣的函數(shù),但是你仍然可以再調(diào)用它是帶入任意多個(gè)參數(shù),當(dāng)然,參數(shù)類型也是任意的。至于是否出錯(cuò),那是這個(gè)函數(shù)中所執(zhí)行的內(nèi)容來決定的,javascript 并不根據(jù)你指定的參數(shù)個(gè)數(shù)和參數(shù)類型來判斷你調(diào)用的是哪個(gè)函數(shù)。
因此,要定義重載方法,就不能像強(qiáng)類型語言中那樣做了。但是你仍然可以實(shí)現(xiàn)重載。就是通過函數(shù)的 arguments 屬性。例如:
復(fù)制代碼 代碼如下:
function add() {
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}
這樣你就實(shí)現(xiàn)了任意多個(gè)參數(shù)加法函數(shù)的重載了。
當(dāng)然,你還可以在函數(shù)中通過 instanceof 或者 constructor 來判斷每個(gè)參數(shù)的類型,來決定后面執(zhí)行什么操作,實(shí)現(xiàn)更為復(fù)雜的函數(shù)或方法重載??傊?,javascript 的重載,是在函數(shù)中由用戶自己通過操作 arguments 這個(gè)屬性來實(shí)現(xiàn)的。
覆蓋
實(shí)現(xiàn)覆蓋也很容易,例如:
復(fù)制代碼 代碼如下:
function parentClass() {
this.method = function() {
alert("parentClass method");
}
}
function subClass() {
this.method = function() {
alert("subClass method");
}
}
subClass.prototype = new parentClass();
subClass.prototype.constructor = subClass;
var o = new subClass();
o.method();
這樣,子類中定義的 method 就覆蓋了從父類中繼承來的 method 方法了。
你可能會說,這樣子覆蓋是不錯(cuò),但 java 中,覆蓋的方法里面可以調(diào)用被覆蓋的方法(父類的方法),在這里怎么實(shí)現(xiàn)呢?也很容易,而且比 java 中還要靈活,java 中限制,你只能在覆蓋被覆蓋方法的方法中才能使用 super 來調(diào)用次被覆蓋的方法。我們不但可以實(shí)現(xiàn)這點(diǎn),而且還可以讓子類中所有的方法中都可以調(diào)用父類中被覆蓋的方法。看下面的例子:
復(fù)制代碼 代碼如下:
function parentClass() {
this.method = function() {
alert("parentClass method");
}
}
function subClass() {
var method = this.method;
this.method = function() {
method.call(this);
alert("subClass method");
}
}
subClass.prototype = new parentClass();
subClass.prototype.constructor = subClass;
var o = new subClass();
o.method();
你會發(fā)現(xiàn),原來這么簡單,只要在定義覆蓋方法前,定義一個(gè)私有變量,然后把父類中定義的將要被覆蓋的方法賦給它,然后我們就可以在后面繼續(xù)調(diào)用它了,而且這個(gè)是這個(gè)方法是私有的,對于子類的對象是不可見的。這樣跟其它高級語言實(shí)現(xiàn)的覆蓋就一致了。
最后需要注意,我們在覆蓋方法中調(diào)用這個(gè)方法時(shí),需要用 call 方法來改變執(zhí)行上下文為 this(雖然在這個(gè)例子中沒有必要),如果直接調(diào)用這個(gè)方法,執(zhí)行上下文就會變成全局對象了。
您可能感興趣的文章:
- JavaScript面向?qū)ο笾甈rototypes和繼承
- Javascript面向?qū)ο缶幊蹋ǘ?構(gòu)造函數(shù)的繼承
- javascript 面向?qū)ο?實(shí)現(xiàn)namespace,class,繼承,重載
- 徹底理解js面向?qū)ο笾^承
- JS實(shí)現(xiàn)面向?qū)ο罄^承的5種方式分析
- Javascript面向?qū)ο缶幊蹋ㄈ?非構(gòu)造函數(shù)的繼承
- JS 面向?qū)ο笾^承---多種組合繼承詳解
- javascript 面向?qū)ο笕吕砭氈^承與多態(tài)
- javascript 面向?qū)ο蠓庋b與繼承
- javaScript面向?qū)ο罄^承方法經(jīng)典實(shí)現(xiàn)
- 《javascript設(shè)計(jì)模式》學(xué)習(xí)筆記二:Javascript面向?qū)ο蟪绦蛟O(shè)計(jì)繼承用法分析
相關(guān)文章
Javascript 類與靜態(tài)類的實(shí)現(xiàn)
在Javascript里,對面向?qū)ο蟛]有一個(gè)直接的實(shí)現(xiàn),對于代碼方面也是非常的靈活。2010-04-04javascript 混合的構(gòu)造函數(shù)和原型方式,動態(tài)原型方式
JS編程中最常用兩種對象類定義的方式。不管是利用下面2種方式的那一種,都可以達(dá)到相同的效果!2009-12-12JavaScript isPrototypeOf和hasOwnProperty使用區(qū)別
JavaScript isPrototypeOf和hasOwnProperty的使用技巧,需要的朋友的朋友可以參考下。2010-03-03JavaScript面向?qū)ο蟪绦蛟O(shè)計(jì)三 原型模式(上)
在javaScript面向?qū)ο笤O(shè)計(jì)一和Javascript面向?qū)ο笤O(shè)計(jì)二中分別介紹了工廠模式和構(gòu)造函數(shù)模式,以及他們格式的優(yōu)缺點(diǎn),今天繼續(xù)講解原型模式2011-12-12關(guān)于javascript function對象那些迷惑分析
關(guān)于javascript function對象那些迷惑分析,學(xué)習(xí)js面向?qū)ο蟮呐笥芽梢詤⒖枷隆?/div> 2011-10-10最新評論