javascript 面向?qū)ο缶幊袒A(chǔ):繼承
更新時(shí)間:2009年08月21日 01:37:42 作者:
"繼承是面向?qū)ο箝_發(fā)的又一個(gè)重要概念,它可以將現(xiàn)實(shí)生活的概念對(duì)應(yīng)帶程序邏輯中"。“ 雖然在JavaScript中沒有專門的機(jī)制來實(shí)現(xiàn)類的繼承,但可以通過拷貝一個(gè)類的prototype 到另外一個(gè)類來實(shí)現(xiàn)繼承”。
我們看到這里繼承的概念是多么的直白,“拷貝一個(gè)類的prototype 到另外一個(gè)類”,好,Code is cheap,看代碼:
function class1() { }
function class2() { }
class2.prototype = class1.prototype;
class2.moreProperty1 = " class 2 additional string " ;
class2.moreMethod1 = function () { alert( " class 2 additional method " ); }
/*
這樣,首先是class2具有了和class1 一樣的prototype,不考慮構(gòu)造函數(shù),兩個(gè)類是等價(jià)的。
隨后,又通過prototype給class2賦予了兩個(gè)額外的方法。所以class2是在class1的基礎(chǔ)上
增加了屬性和方法,這就實(shí)現(xiàn)了類的繼承。
*/
function test() {
var obj = new class2();
// JavaScript提供了instanceof 操作符來判斷一個(gè)對(duì)象是否是某個(gè)類的實(shí)例
alert(obj instanceof class2); // true
alert(obj instanceof class1); // ?
}
運(yùn)行代碼,結(jié)果是不是在我們的意料之中?表面上看,上面的實(shí)現(xiàn)完全可行,js也可以正確的理解和實(shí)現(xiàn)這種繼承關(guān)系,obj同時(shí)是class1和 class2的實(shí)例,但實(shí)質(zhì)上則不然(我們學(xué)習(xí)的目的是要知其然更要知其所以然)。js的這種理解實(shí)際上是基于一種很簡單的策略,看下面的代碼,先使用 prototype讓class2 繼承于class1,再在class2 中重復(fù)定義method 方法:
// 定義class1
function class1() {
// 構(gòu)造函數(shù)
}
// 定義class1 的成員
class1.prototype = {
m1: function () { // 方法1
alert( " class1 method1 " );
}
}
// 定義class2
function class2() {
// 構(gòu)造函數(shù)
}
// 讓class2 繼承于class1
class2.prototype = class1.prototype;
// 給class2 重復(fù)定義方法method
class2.prototype.method = function () {
alert( " whose method2? class1 or class2 " );
}
// 創(chuàng)建兩個(gè)類的實(shí)例
var obj1 = new class1();
var obj2 = new class2();
function test() {
// 分別調(diào)用兩個(gè)對(duì)象的method 方法
obj1.method();
obj2.method();
}
從代碼執(zhí)行結(jié)果看,method方法在class1,2中運(yùn)行的結(jié)果是相同的。
由此可見,當(dāng)對(duì)class2 進(jìn)行prototype 的改變時(shí),class1的prototype也隨之改變,即使對(duì)class2 的prototype 增減一些成員,class1的成員也隨之改變。所以class1 和class2 僅僅是構(gòu)造函數(shù)不同的兩個(gè)類,它們保持著相同的成員定義。說到這里,相信讀者已經(jīng)發(fā)現(xiàn)了其中的奧妙:class1 和class2 的prototype 是完全相同的,是對(duì)同一個(gè)對(duì)象的引用。其實(shí)從這條賦值語句就可以看出來:
//讓class2 繼承于class1
class2.prototype=class1.prototype;
在js中,除了基本的數(shù)據(jù)類型(數(shù)字、字符串、布爾類型等),所有的賦值以及函數(shù)參數(shù)都是引用傳遞,而不是值傳遞。所以上面的語句僅僅是讓class2 的prototype 對(duì)象引用class1 的prototype,造成了類成員定義始終保持一致的效果。從這里也看到了instanceof 操作符的執(zhí)行機(jī)制,它就是判斷一個(gè)對(duì)象是否是一個(gè)prototype 的實(shí)例,因?yàn)檫@里的obj1 和obj2 都是對(duì)應(yīng)于同一個(gè)prototype,所以它們instanceof 的結(jié)果都是相同的。由此可見,使用prototype 引用拷貝實(shí)現(xiàn)繼承不是一種正確的辦法。但在要求不嚴(yán)格的情況下,卻也是一種合理的方法,唯一的約束是不允許類成員的覆蓋定義(這里其實(shí)也是js的靈活性的體現(xiàn))。其實(shí),我們完全可以利用反射機(jī)制和prototype 來實(shí)現(xiàn)js正確的類繼承:
function class1() {
// 構(gòu)造函數(shù)
}
class1.prototype = {
method: function () {
alert( " method1 " );
},
method2: function () {
alert( " method2 " );
}
}
function class2() {
// 構(gòu)造函數(shù)
}
// 讓class2 繼承于class1
for ( var p in class1.prototype) {
class2.prototype[p] = class1.prototype[p]; // 利用反射機(jī)制和prototype實(shí)現(xiàn)繼承
}
// 覆蓋定義class1中的method 方法
class2.prototype.method = function () {
alert( " class2 new method1 " );
}
// 創(chuàng)建兩個(gè)類的實(shí)例
var obj1 = new class1();
var obj2 = new class2();
function test() {
// 分別調(diào)用兩個(gè)對(duì)象的method 方法
obj1.method();
obj2.method();
// 分別調(diào)用兩個(gè)對(duì)象的method2 方法
obj1.method2();
obj2.method2();
}
從運(yùn)行結(jié)果可見,obj2中重復(fù)定義的method 已經(jīng)覆蓋了繼承的method方法,同時(shí)method2 方法未受影響。而且obj1中的method 方法仍然保持了原有的定義。這樣,就實(shí)現(xiàn)了正確意義的類的繼承。為了方便開發(fā),可以為每個(gè)類添加一個(gè)共有的方法,用以實(shí)現(xiàn)類的繼承:
// 為類添加靜態(tài)方法inherit表示繼承于某類
Function.prototype.inherit = function (baseClass) {
for ( var p in baseClass.prototype) {
this .prototype[p] = baseClass.prototype[p];
}
}
function class1() {
// 構(gòu)造函數(shù)
}
class1.prototype = {
method: function () {
alert( " method1 " );
},
method2: function () {
alert( " method2 " );
}
}
function class2() {
// 構(gòu)造函數(shù)
}
// 讓class2 繼承于class1
// for (var p in class1.prototype) {
// class2.prototype[p] = class1.prototype[p]; // 利用反射機(jī)制和prototype實(shí)現(xiàn)繼承
// }
class2.inherit(class1); // 等價(jià)于上面注釋掉的那一個(gè)for循環(huán)
// 覆蓋定義class1中的method 方法
class2.prototype.method = function () {
alert( " class2 new method1 " );
}
// 創(chuàng)建兩個(gè)類的實(shí)例
var obj1 = new class1();
var obj2 = new class2();
function test() {
// 分別調(diào)用兩個(gè)對(duì)象的method 方法
obj1.method();
obj2.method();
// 分別調(diào)用兩個(gè)對(duì)象的method2 方法
obj1.method2();
obj2.method2();
}
上面的代碼使邏輯變的更加清楚,也更容易理解。通過這種方法實(shí)現(xiàn)的繼承,有一個(gè)缺點(diǎn),就是在class2 中添加類成員定義時(shí),不能給prototype 直接賦值,而只能對(duì)其屬性進(jìn)行賦值,例如不能為:
class2.prototype={
//成員定義
}
而只能為:
class2.prototype.propertyName=someValue;
class2.prototype.methodName=function(){
//語句
}
由此可見,這樣實(shí)現(xiàn)繼承仍然要以犧牲一定的代碼可讀性為代價(jià)。有沒有“不僅基類可以用對(duì)象直接賦值給property,而且在派生類中也可以同樣實(shí)現(xiàn),使代碼邏輯更加清晰,也更能體現(xiàn)面向?qū)ο蟮恼Z言特點(diǎn)”的js繼承方式?引號(hào)里的說法是多么的誘人啊,繼續(xù)學(xué)習(xí)去了。
function class1() { }
function class2() { }
class2.prototype = class1.prototype;
class2.moreProperty1 = " class 2 additional string " ;
class2.moreMethod1 = function () { alert( " class 2 additional method " ); }
/*
這樣,首先是class2具有了和class1 一樣的prototype,不考慮構(gòu)造函數(shù),兩個(gè)類是等價(jià)的。
隨后,又通過prototype給class2賦予了兩個(gè)額外的方法。所以class2是在class1的基礎(chǔ)上
增加了屬性和方法,這就實(shí)現(xiàn)了類的繼承。
*/
function test() {
var obj = new class2();
// JavaScript提供了instanceof 操作符來判斷一個(gè)對(duì)象是否是某個(gè)類的實(shí)例
alert(obj instanceof class2); // true
alert(obj instanceof class1); // ?
}
運(yùn)行代碼,結(jié)果是不是在我們的意料之中?表面上看,上面的實(shí)現(xiàn)完全可行,js也可以正確的理解和實(shí)現(xiàn)這種繼承關(guān)系,obj同時(shí)是class1和 class2的實(shí)例,但實(shí)質(zhì)上則不然(我們學(xué)習(xí)的目的是要知其然更要知其所以然)。js的這種理解實(shí)際上是基于一種很簡單的策略,看下面的代碼,先使用 prototype讓class2 繼承于class1,再在class2 中重復(fù)定義method 方法:
// 定義class1
function class1() {
// 構(gòu)造函數(shù)
}
// 定義class1 的成員
class1.prototype = {
m1: function () { // 方法1
alert( " class1 method1 " );
}
}
// 定義class2
function class2() {
// 構(gòu)造函數(shù)
}
// 讓class2 繼承于class1
class2.prototype = class1.prototype;
// 給class2 重復(fù)定義方法method
class2.prototype.method = function () {
alert( " whose method2? class1 or class2 " );
}
// 創(chuàng)建兩個(gè)類的實(shí)例
var obj1 = new class1();
var obj2 = new class2();
function test() {
// 分別調(diào)用兩個(gè)對(duì)象的method 方法
obj1.method();
obj2.method();
}
從代碼執(zhí)行結(jié)果看,method方法在class1,2中運(yùn)行的結(jié)果是相同的。
由此可見,當(dāng)對(duì)class2 進(jìn)行prototype 的改變時(shí),class1的prototype也隨之改變,即使對(duì)class2 的prototype 增減一些成員,class1的成員也隨之改變。所以class1 和class2 僅僅是構(gòu)造函數(shù)不同的兩個(gè)類,它們保持著相同的成員定義。說到這里,相信讀者已經(jīng)發(fā)現(xiàn)了其中的奧妙:class1 和class2 的prototype 是完全相同的,是對(duì)同一個(gè)對(duì)象的引用。其實(shí)從這條賦值語句就可以看出來:
//讓class2 繼承于class1
class2.prototype=class1.prototype;
在js中,除了基本的數(shù)據(jù)類型(數(shù)字、字符串、布爾類型等),所有的賦值以及函數(shù)參數(shù)都是引用傳遞,而不是值傳遞。所以上面的語句僅僅是讓class2 的prototype 對(duì)象引用class1 的prototype,造成了類成員定義始終保持一致的效果。從這里也看到了instanceof 操作符的執(zhí)行機(jī)制,它就是判斷一個(gè)對(duì)象是否是一個(gè)prototype 的實(shí)例,因?yàn)檫@里的obj1 和obj2 都是對(duì)應(yīng)于同一個(gè)prototype,所以它們instanceof 的結(jié)果都是相同的。由此可見,使用prototype 引用拷貝實(shí)現(xiàn)繼承不是一種正確的辦法。但在要求不嚴(yán)格的情況下,卻也是一種合理的方法,唯一的約束是不允許類成員的覆蓋定義(這里其實(shí)也是js的靈活性的體現(xiàn))。其實(shí),我們完全可以利用反射機(jī)制和prototype 來實(shí)現(xiàn)js正確的類繼承:
function class1() {
// 構(gòu)造函數(shù)
}
class1.prototype = {
method: function () {
alert( " method1 " );
},
method2: function () {
alert( " method2 " );
}
}
function class2() {
// 構(gòu)造函數(shù)
}
// 讓class2 繼承于class1
for ( var p in class1.prototype) {
class2.prototype[p] = class1.prototype[p]; // 利用反射機(jī)制和prototype實(shí)現(xiàn)繼承
}
// 覆蓋定義class1中的method 方法
class2.prototype.method = function () {
alert( " class2 new method1 " );
}
// 創(chuàng)建兩個(gè)類的實(shí)例
var obj1 = new class1();
var obj2 = new class2();
function test() {
// 分別調(diào)用兩個(gè)對(duì)象的method 方法
obj1.method();
obj2.method();
// 分別調(diào)用兩個(gè)對(duì)象的method2 方法
obj1.method2();
obj2.method2();
}
從運(yùn)行結(jié)果可見,obj2中重復(fù)定義的method 已經(jīng)覆蓋了繼承的method方法,同時(shí)method2 方法未受影響。而且obj1中的method 方法仍然保持了原有的定義。這樣,就實(shí)現(xiàn)了正確意義的類的繼承。為了方便開發(fā),可以為每個(gè)類添加一個(gè)共有的方法,用以實(shí)現(xiàn)類的繼承:
// 為類添加靜態(tài)方法inherit表示繼承于某類
Function.prototype.inherit = function (baseClass) {
for ( var p in baseClass.prototype) {
this .prototype[p] = baseClass.prototype[p];
}
}
function class1() {
// 構(gòu)造函數(shù)
}
class1.prototype = {
method: function () {
alert( " method1 " );
},
method2: function () {
alert( " method2 " );
}
}
function class2() {
// 構(gòu)造函數(shù)
}
// 讓class2 繼承于class1
// for (var p in class1.prototype) {
// class2.prototype[p] = class1.prototype[p]; // 利用反射機(jī)制和prototype實(shí)現(xiàn)繼承
// }
class2.inherit(class1); // 等價(jià)于上面注釋掉的那一個(gè)for循環(huán)
// 覆蓋定義class1中的method 方法
class2.prototype.method = function () {
alert( " class2 new method1 " );
}
// 創(chuàng)建兩個(gè)類的實(shí)例
var obj1 = new class1();
var obj2 = new class2();
function test() {
// 分別調(diào)用兩個(gè)對(duì)象的method 方法
obj1.method();
obj2.method();
// 分別調(diào)用兩個(gè)對(duì)象的method2 方法
obj1.method2();
obj2.method2();
}
上面的代碼使邏輯變的更加清楚,也更容易理解。通過這種方法實(shí)現(xiàn)的繼承,有一個(gè)缺點(diǎn),就是在class2 中添加類成員定義時(shí),不能給prototype 直接賦值,而只能對(duì)其屬性進(jìn)行賦值,例如不能為:
class2.prototype={
//成員定義
}
而只能為:
class2.prototype.propertyName=someValue;
class2.prototype.methodName=function(){
//語句
}
由此可見,這樣實(shí)現(xiàn)繼承仍然要以犧牲一定的代碼可讀性為代價(jià)。有沒有“不僅基類可以用對(duì)象直接賦值給property,而且在派生類中也可以同樣實(shí)現(xiàn),使代碼邏輯更加清晰,也更能體現(xiàn)面向?qū)ο蟮恼Z言特點(diǎn)”的js繼承方式?引號(hào)里的說法是多么的誘人啊,繼續(xù)學(xué)習(xí)去了。
您可能感興趣的文章:
- Javascript簡單實(shí)現(xiàn)面向?qū)ο缶幊汤^承實(shí)例代碼
- Javascript面向?qū)ο缶幊蹋ㄈ?非構(gòu)造函數(shù)的繼承
- Javascript面向?qū)ο缶幊蹋ǘ?構(gòu)造函數(shù)的繼承
- JavaScript面向?qū)ο笾甈rototypes和繼承
- javascript 面向?qū)ο?實(shí)現(xiàn)namespace,class,繼承,重載
- 徹底理解js面向?qū)ο笾^承
- javascript 面向?qū)ο笕吕砭氈屠^承
- JS實(shí)現(xiàn)面向?qū)ο罄^承的5種方式分析
- JS 面向?qū)ο笾^承---多種組合繼承詳解
- javascript 面向?qū)ο笕吕砭氈^承與多態(tài)
- JS面向?qū)ο缶幊袒A(chǔ)篇(三) 繼承操作實(shí)例詳解
相關(guān)文章
Javascript 類與靜態(tài)類的實(shí)現(xiàn)(續(xù))
由于MM的事件已干完,接著我們的靜態(tài)類的實(shí)現(xiàn)。這東西在Javascript里用得會(huì)非常的頻繁,因?yàn)獒槍?duì)現(xiàn)在的網(wǎng)頁,多個(gè)基于同一個(gè)類對(duì)象的頁面不多,往往不同塊對(duì)象的交互就可以解決問題了,這就需要在JS針對(duì)元素定義幾個(gè)靜態(tài)類就可以完事了,進(jìn)入正題。2010-04-04
JavaScript 面向?qū)ο缶幊蹋?) 基礎(chǔ)
自從有了Ajax這個(gè)概念,JavaScript作為Ajax的利器,其作用一路飆升。JavaScript最基本的使用,以及語法、瀏覽器對(duì)象等等東東在這里就不累贅了。把主要篇幅放在如何實(shí)現(xiàn)JavaScript的面向?qū)ο缶幊谭矫妗?/div> 2010-05-05
學(xué)習(xí)面向?qū)ο笾嫦驅(qū)ο蟮幕靖拍?對(duì)象和其他基本要素
學(xué)習(xí)面向?qū)ο笾嫦驅(qū)ο蟮幕靖拍?對(duì)象和其他基本要素2010-11-11
JavaScript對(duì)象鏈?zhǔn)讲僮鞔a(jquery)
自從使用了jQuery以后,對(duì)它的鏈?zhǔn)讲僮骱苁且蕾嚕灾劣诔3SX得其他庫不好用。。2010-07-07
JavaScript 面向?qū)ο蟮闹接谐蓡T和公開成員
這節(jié)來說下JavaScript的私有成員和公開成員,雖然JavaScript沒有private和public關(guān)鍵字,但還是那句話——作為開發(fā)人員我們要有面向?qū)ο蟮乃枷耄?2010-05-05
JavaScript 核心參考教程 內(nèi)置對(duì)象
JavaScript 是根據(jù) "ECMAScript"標(biāo)準(zhǔn)制定的網(wǎng)頁腳本語言。這個(gè)標(biāo)準(zhǔn)由 ECMA 組織發(fā)展和維護(hù)。ECMA-262 是正式的 JavaScript 標(biāo)準(zhǔn)。2009-10-10
javascript 模式設(shè)計(jì)之工廠模式學(xué)習(xí)心得
接口的實(shí)現(xiàn),從而使不同子類可以被同等的對(duì)待,恰當(dāng)?shù)氖褂霉S模式,但不要拘泥與形式,理解本質(zhì)。2010-04-04最新評(píng)論

