js類(lèi)式繼承的具體實(shí)現(xiàn)方法
在開(kāi)始擺弄代碼之前,應(yīng)該搞清楚使用繼承的目的和能帶來(lái)什么好處。一般來(lái)說(shuō),在設(shè)計(jì)類(lèi)的時(shí)候,我們希望能減少重復(fù)性的代碼,并且盡量弱化類(lèi)之間的耦合。而要做到這兩者都兼顧是很難的,我們需要根據(jù)具體的條件和環(huán)境下決定我們應(yīng)該采取什么方法。根據(jù)我們對(duì)面向?qū)ο笳Z(yǔ)言中繼承的了解,繼承會(huì)帶類(lèi)直接的強(qiáng)耦合,但js由于其特有的靈活性,可以設(shè)計(jì)出強(qiáng)耦合和弱耦合,高效率和低效率的代碼。而具體用什么,看情況。
下面提供js實(shí)現(xiàn)繼承的三種方法:類(lèi)式繼承,原型繼承,摻元類(lèi)。這里先簡(jiǎn)述類(lèi)式繼承,后兩種在往后的隨便中簡(jiǎn)述,請(qǐng)多多關(guān)注、指導(dǎo),謝謝。
類(lèi)式繼承。
js類(lèi)式繼承的實(shí)現(xiàn)依靠原型鏈來(lái)實(shí)現(xiàn)的。什么是原型鏈?js中對(duì)象有個(gè)屬性prototy,這個(gè)屬性返回對(duì)象類(lèi)型的引用,用于提供對(duì)象的類(lèi)的一組基本功能。
貌似對(duì)prototype有印象,對(duì)了,我們經(jīng)常這樣用代碼。
var Person = function(){
this.name = "liyatang";
};
Person.prototype = {
//可以在這里提供Person的基本功能
getName : function(){
return this.name;
}
}
我們把類(lèi)的基本功能放在prototype屬性里,表示Person這個(gè)對(duì)象的引用有XXX功能。
在理解原型后,需要理解下什么是原型鏈。在訪問(wèn)對(duì)象的某個(gè)成員(屬性或方法)時(shí),如果這個(gè)成員未見(jiàn)于當(dāng)前對(duì)象,那么js會(huì)在prototype屬性所指的那個(gè)對(duì)象中查找它,如果還沒(méi)有找到,就繼續(xù)到下一級(jí)的prototype所指的對(duì)象中查找,直至找到。如果沒(méi)有找到就會(huì)返回undifined。
那么原型鏈給我們什么提示呢?很容易聯(lián)想到,原型鏈意味著讓一個(gè)類(lèi)繼承另一個(gè)類(lèi),只需將子類(lèi)的prototype設(shè)置為指向父類(lèi)的一個(gè)實(shí)例即可。這就把父類(lèi)的成員綁定到子類(lèi)上了,因?yàn)樵谧宇?lèi)上查找不到某個(gè)成員時(shí)會(huì)往父類(lèi)中查找。(以上這兩段用詞不嚴(yán)謹(jǐn),只在用通俗易懂的言語(yǔ)描述)
下面我們需要個(gè)Chinese類(lèi),需要繼承Person類(lèi)的name和getName成員。
var Chinese = function(name, nation){
//繼承,需要調(diào)用父類(lèi)的構(gòu)造函數(shù),可以用call調(diào)用,this指向Chinese
//使Person在此作用域上,才可以調(diào)用Person的成員
Person.call(this,name);
this.nation = nation;
};
Chinese.prototype = Person.prototype;
//這里不可和以前一樣,因?yàn)楦采w掉了prototype屬性
//Chinese.prototype = {
// getNation : function(){
// return this.nation;
// }
//};
//以后的方法都需要這樣加
Chinese.prototype.getNation = function(){
return this.nation;
};
繼承關(guān)系就建立了,我們這樣調(diào)用它
var c = new Chinese("liyatang","China");
alert(c.getName());// liyatang
于是類(lèi)式繼承就這樣完成了。難道真的完成了嘛,用firebug在alert那里設(shè)斷點(diǎn),會(huì)發(fā)現(xiàn)原來(lái)的Person.prototype被修改了,添加了getNation方法。
這是因?yàn)樵谏厦娴拇aChinese.prototype = Person.prototype; 這是引用類(lèi)型,修改Chinese同時(shí)也修改了Person。這本身就是不能容忍的,且使類(lèi)之間形成強(qiáng)耦合性,這不是我們要的效果。
我們可以另起一個(gè)對(duì)象或?qū)嵗粋€(gè)實(shí)例來(lái)弱化耦合性。
//第一種
//Chinese.prototype = new Person();
//第二種
//var F = function(){};
//F.prototype = Person.prototype;
//Chinese.prototype = F.prototype;
這兩種方法有什么區(qū)別呢。在第二種中添加了一個(gè)空函數(shù)F,這樣做可以避免創(chuàng)建父類(lèi)的一個(gè)實(shí)例,因?yàn)橛锌赡芨割?lèi)會(huì)比較龐大,而且父類(lèi)的構(gòu)造函數(shù)會(huì)有一些副作用,或者說(shuō)會(huì)執(zhí)行大量的計(jì)算任務(wù)。所以力薦第二種方法。
到此,完了嘛,還沒(méi)有!在對(duì)象的屬性prototype下面有個(gè)屬性constructor,它保存了對(duì)構(gòu)造特定對(duì)象實(shí)例的函數(shù)的引用。根據(jù)這個(gè)說(shuō)法Chiese.prototype.constructor應(yīng)該等于Chinese,實(shí)際上不是。
回憶之前在設(shè)置Chiese的原型鏈時(shí),我們把Person.prototype 覆蓋掉了Chiese.prototype。所以此時(shí)的Chiese.prototype.constructor是Person。我們還需要添加以下代碼
//對(duì)這里的if條件不需要細(xì)究,知道Chinese.prototype.constructor = Chinese就行
if(Chinese.prototype.constructor == Object.prototype.constructor){
Chinese.prototype.constructor = Chinese;
}
整理全部代碼如下
var Person = function(name){
this.name = name;
};
Person.prototype = {
getName : function(){
return this.name;
}
};
var Chinese = function(name, nation){
Person.call(this,name);
this.nation = nation;
};
var F = function(){};
F.prototype = Person.prototype;
Chinese.prototype = F.prototype;
if(Chinese.prototype.constructor == Object.prototype.constructor){
Chinese.prototype.constructor = Chinese;
}
Chinese.prototype.getNation = function(){
return this.nation;
};
var c = new Chinese("liyatang","China");
alert(c.getName());
如果可以把繼承的代碼放在一個(gè)函數(shù)里,方便代碼復(fù)用,最后整理代碼如下
function extend(subClass,superClass){
var F = function(){};
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass;
subClass.superclass = superClass.prototype; //加多了個(gè)屬性指向父類(lèi)本身以便調(diào)用父類(lèi)函數(shù)
if(superClass.prototype.constructor == Object.prototype.constructor){
superClass.prototype.constructor = superClass;
}
}
var Person = function(name){
this.name = name;
};
Person.prototype = {
getName : function(){
return this.name;
}
};
var Chinese = function(name, nation){
Person.call(this,name);
this.nation = nation;
};
extend(Chinese, Person);
Chinese.prototype.getNation = function(){
return this.nation;
};
var c = new Chinese("liyatang","China");
alert(c.getName());
發(fā)表后修改:
在一樓的評(píng)論下,我對(duì)那個(gè)extend函數(shù)又有新的看法。之前在討論如何設(shè)置原型鏈時(shí)提出了兩種方法
//第一種
//Chinese.prototype = new Person();
//第二種
//var F = function(){};
//F.prototype = Person.prototype;
//Chinese.prototype = F.prototype;
雖然第二種減少了調(diào)用父類(lèi)的構(gòu)造函數(shù)這條路,但在設(shè)計(jì)Chinese類(lèi)時(shí)用了Person.call(this,name);這里也相當(dāng)于調(diào)用了父類(lèi)的構(gòu)造函數(shù)。
然而用第一種方法的話可以減少在Chinese中再寫(xiě)Person.call(this,name);,這部分代碼在子類(lèi)中往往會(huì)遺忘。不妨把這種功能代碼放在了extend里。就只寫(xiě)
Chinese.prototype = new Person();也達(dá)到同樣的目的:耦合不強(qiáng)。
但遺忘的一點(diǎn)是,Chinese.prototype = new Person();這樣寫(xiě)對(duì)嘛。答案是不對(duì)!很明顯 new Person()需要傳一個(gè)name參數(shù)的。我們不可能在extend函數(shù)里做這部分工作,只好在Chinese類(lèi)里調(diào)用父類(lèi)的構(gòu)造函數(shù)了。這樣也符合面向?qū)ο蟮乃悸贰?/P>
所以,還是力薦用第二種方法。
第一次這樣寫(xiě)有關(guān)技術(shù)類(lèi)的文章,基本是按自己的思路鋪展開(kāi)來(lái),難免會(huì)有一些沒(méi)有考慮到的地方和解釋的不清楚的地方,望留言反饋,謝謝。
相關(guān)文章
JavaScript中的正則表達(dá)式簡(jiǎn)明總結(jié)
這篇文章主要介紹了JavaScript中的正則表達(dá)式,簡(jiǎn)明總結(jié)了正則中的語(yǔ)法含義和RegExp對(duì)象,需要的朋友可以參考下2014-04-04typescript常見(jiàn)高級(jí)技巧總結(jié)
這篇文章主要介紹了typescript常見(jiàn)高級(jí)技巧總結(jié),需要的朋友可以參考下2022-10-10簡(jiǎn)介JavaScript中fixed()方法的使用
這篇文章主要介紹了JavaScript中fixed()方法的使用,是JS入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-06-06實(shí)例分析javascript中的call()和apply()方法
因項(xiàng)目需求去研究了下javascript中的call和apply方法,去百度看了幾篇介紹JS中call和apply的文章,總覺(jué)得不是很好懂,這里寫(xiě)下我自己的理解,供網(wǎng)友們參考。2014-11-11簡(jiǎn)介JavaScript中Math.cos()余弦方法的使用
這篇文章主要介紹了簡(jiǎn)介JavaScript中Math.cos()余弦方法的使用,是JS入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-06-06JavaScript中的call方法和apply方法使用對(duì)比
這篇文章主要介紹了JavaScript中的call方法和apply方法使用對(duì)比,需要的朋友可以參考下2015-08-08JavaScript中用于四舍五入的Math.round()方法講解
這篇文章主要介紹了JavaScript中用于四舍五入的Math.round()方法講解,是JS入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-06-06