繼承

繼承是面向?qū)ο笾幸粋€比較核心的概念者疤。其他正統(tǒng)面向?qū)ο笳Z言都會用兩種方式實現(xiàn)繼承:一個是接口實現(xiàn),一個是繼承叠赦。而ECMAScript只支持繼承驹马,不支持接口實現(xiàn)革砸,而實現(xiàn)繼承的方式依靠原型鏈完成。

)原型鏈繼承

function Box() {//Box構造

this.name = 'xiaoming';

}

function Desk() {//Desk構造

this.age = 100;

}

Desk.prototype = new Box();//Desc繼承了Box糯累,通過原型算利,形成鏈條

var desk = new Desk();

alert(desk.age); ??????????????????????????//100

alert(desk.name);//xiaoming得到被繼承的屬性

function Table() {//Table構造

this.level = 'AAAAA';

}

Table.prototype = new Desk();//繼續(xù)原型鏈繼承

var table = new Table();

alert(table.name);//繼承了Box和Desk


原型鏈繼承流程圖

如果要實例化table,那么Desk實例中有age=100泳姐,原型中增加相同的屬性age=200效拭,最后結(jié)果是多少呢?

Desk.prototype.age = 200;//實例和原型中均包含age

PS:以上原型鏈繼承還缺少一環(huán)胖秒,那就是Obejct缎患,所有的構造函數(shù)都繼承自Obejct。而繼承Object是自動完成的阎肝,并不需要程序員手動繼承挤渔。

經(jīng)過繼承后的實例,他們的從屬關系會怎樣呢风题?

alert(table instanceof Object);//true

alert(desk instanceof Table);//false蚂蕴,desk是table的超類

alert(table instanceof Desk);//true

alert(table instanceof Box);//true

在JavaScript里,被繼承的函數(shù)稱為超類型(父類俯邓,基類也行骡楼,其他語言叫法),繼承的函數(shù)稱為子類型(子類稽鞭,派生類)鸟整。繼承也有之前問題,比如字面量重寫原型會中斷關系朦蕴,使用引用類型的原型篮条,并且子類型還無法給超類型傳遞參數(shù)。

原型鏈繼承的缺陷:

1吩抓、 無法從子類中調(diào)用父類的構造函數(shù)涉茧, 這樣就沒有辦法把子類中屬性賦值給父類。

2疹娶、 父類中屬性是在子類的原型中的伴栓,這違背了我們前面所講的封裝的理念( 屬性在對象中,方法在原型中)雨饺, 會出現(xiàn)前面值的混淆問題钳垮。

2)對象冒充的方式繼承

為了解決引用共享和超類型無法傳參的問題,我們采用一種叫借用構造函數(shù)的技術额港,或者成為對象冒充(偽造對象饺窿、經(jīng)典繼承)的技術來解決這兩種問題。

function Box(age) {

this.name = ['xiaoming', 'Jack', 'Hello']

this.age = age;

}

function Desk(age) {

Box.call(this, age);//對象冒充移斩,給超類型傳參

}

var desk = new Desk(200);

alert(desk.age);

alert(desk.name);

desk.name.push('AAA');//添加的新數(shù)據(jù)绢馍,只給desk

alert(desk.name);

3)組合繼承

借用構造函數(shù)雖然解決了剛才兩種問題,但沒有原型肠套,復用則無從談起痕貌。所以入宦,我們需要原型鏈+借用構造函數(shù)的模式哺徊,這種模式成為組合繼承

function Box(age) {

this.name = ['xiaoming', 'Jack', 'Hello']

this.age = age;

}

Box.prototype.run = function () {

return this.name + this.age;

};

function Desk(age) {

Box.call(this, age);//對象冒充

}

Desk.prototype = new Box();//原型鏈繼承

var desk = new Desk(100);

alert(desk.run());

組合式繼承是JavaScript最常用的繼承模式乾闰;但落追,組合式繼承也有一點小問題,就是超類型在使用過程中會被調(diào)用兩次:一次是創(chuàng)建子類型的時候涯肩,另一次是在子類型構造函數(shù)的內(nèi)部轿钠。

function Box(name) {

this.name = name;

this.family = ['哥哥','妹妹','父母'];

}

Box.prototype.run = function () {

return this.name;

};

function Desk(name, age) {

Box.call(this, name);//第二次調(diào)用Box

this.age = age;

}

Desk.prototype = new Box();//第一次調(diào)用Box

4)空對象繼承

以上代碼是之前的組合繼承,那么用空對象直接繼承prototype方法病苗,解決了兩次調(diào)用的問題疗垛。

function extend(Child, Parent) {

var F = function(){};

F.prototype = Parent.prototype;

Child.prototype = new F();

Child.prototype.constructor = Child;

//Child.uber = Parent.prototype;

}

這個extend函數(shù),就是YUI庫如何實現(xiàn)繼承的方法硫朦。

另外贷腕,說明一點,函數(shù)體最后一行

Child.uber = Parent.prototype;

意思是為子對象設一個uber屬性咬展,這個屬性直接指向父對象的prototype屬性泽裳。(uber是一個德語詞,意思是"向上"破婆、"上一層"涮总。)這等于在子對象上打開一條通道,可以直接調(diào)用父對象的方法祷舀。這一行放在這里瀑梗,只是為了實現(xiàn)繼承的完備性,純屬備用性質(zhì)蔑鹦。

function Box(name) {

this.name = name;

this.arr = ['哥哥','妹妹','父母'];

}

Box.prototype.run = function () {

return this.name;

};

function Desk(name, age) {

Box.call(this, name);

this.age = age;

}

extend( Desk,Box);//通過這里實現(xiàn)繼承

var desk = new Desk('xiaoming',100);

desk.arr.push('姐姐');

alert(desk.arr);

alert(desk.run());//只共享了方法

var desk2 = new Desk('Jack', 200);

alert(desk2.arr);//引用問題解決

5)拷貝繼承

上面是采用prototype對象夺克,實現(xiàn)繼承箕宙。我們也可以換一種思路嚎朽,純粹采用"拷貝"方法實現(xiàn)繼承。簡單說柬帕,如果把父對象的所有屬性和方法哟忍,拷貝進子對象狡门,不也能夠?qū)崿F(xiàn)繼承嗎?這樣我們就有了第五種方法锅很。

首先其馏,還是把Animal的所有不變屬性,都放到它的prototype對象上爆安。

function Animal(){}

Animal.prototype.species = "動物";

然后叛复,再寫一個函數(shù),實現(xiàn)屬性拷貝的目的扔仓。

function extend2(Child, Parent) {

var p = Parent.prototype;

var c = Child.prototype;

for (var i in p) {

c[i] = p[i];

}

c.uber = p; }

這個函數(shù)的作用褐奥,就是將父對象的prototype對象中的屬性,一一拷貝給Child對象的prototype對象翘簇。

6)ECMA6 extends關鍵字 類的繼承

先來說一說撬码,es6的class語法

JavaScript語言的傳統(tǒng)方法是通過構造函數(shù),定義并生成新對象版保。下面是一個例子呜笑。

functionPoint(x,y){

this.x=x;

this.y=y;

}

Point.prototype.toString=function(){

return'('+this.x+', '+this.y+')';

};

varp=newPoint(1,2);

上面這種寫法跟傳統(tǒng)的面向?qū)ο笳Z言(比如C++和Java)差異很大,很容易讓新學習這門語言的程序員感到困惑彻犁。

ES6提供了更接近傳統(tǒng)語言的寫法叫胁,引入了Class(類)這個概念,作為對象的模板汞幢。通過class關鍵字曹抬,可以定義類〖宾基本上谤民,ES6的class可以看作只是一個語法糖,它的絕大部分功能疾宏,ES5都可以做到张足,新的class寫法只是讓對象原型的寫法更加清晰、更像面向?qū)ο缶幊痰恼Z法而已坎藐。上面的代碼用ES6的“類”改寫为牍,就是下面這樣

//定義類

classPoint{

constructor(x,y){

this.x=x;

this.y=y;

}

toString(){

return'('+this.x+', '+this.y+')';

}? }


上面代碼定義了一個“類”,可以看到里面有一個constructor方法岩馍,這就是構造方法碉咆,而this關鍵字則代表實例對象。也就是說蛀恩,ES5的構造函數(shù)Point疫铜,對應ES6的Point類的構造方法。

Point類除了構造方法双谆,還定義了一個toString方法壳咕。注意席揽,定義“類”的方法的時候,前面不需要加上function這個關鍵字谓厘,直接把函數(shù)定義放進去了就可以了幌羞。另外,方法之間不需要逗號分隔竟稳,加了會報錯属桦。

ES6的類,完全可以看作構造函數(shù)的另一種寫法他爸。

classPoint{

// ...

}

typeofPoint// "function"

Point===Point.prototype.constructor// true

上面代碼表明地啰,類的數(shù)據(jù)類型就是函數(shù),類本身就指向構造函數(shù)讲逛。

使用的時候亏吝,也是直接對類使用new命令,跟構造函數(shù)的用法完全一致盏混。

classBar{

doStuff(){

console.log('stuff');

}

}

varb=newBar();

b.doStuff()// "stuff"


構造函數(shù)的prototype屬性蔚鸥,在ES6的“類”上面繼續(xù)存在。事實上许赃,類的所有方法都定義在類的prototype屬性上面止喷。

classPoint{

constructor(){

// ...

}

toString(){

// ...

}

toValue(){

// ...

}

}

//等同于

Point.prototype={

toString(){},

toValue(){}

};

在類的實例上面調(diào)用方法,其實就是調(diào)用原型上的方法

classB{}

letb=newB();

b.constructor===B.prototype.constructor// true

上面代碼中混聊,b是B類的實例弹谁,它的constructor方法就是B類原型的constructor方法。

由于類的方法都定義在prototype對象上面句喜,所以類的新方法可以添加在prototype對象上面预愤。Object.assign方法可以很方便地一次向類添加多個方法。

classPoint{

constructor(){

// ...

}

}

Object.assign(Point.prototype,{

toString(){},

toValue(){}

});


prototype對象的constructor屬性咳胃,直接指向“類”的本身植康,這與ES5的行為是一致的。

Point.prototype.constructor===Point// true


類的實例對象需要注意:

生成類的實例對象的寫法展懈,與ES5完全一樣销睁,也是使用new命令。如果忘記加上new存崖,像函數(shù)那樣調(diào)用Class冻记,將會報錯。

/報錯

varpoint=Point(2,3);

//正確

varpoint=newPoint(2,3);

不存在變量提升

Class不存在變量提升(hoist)来惧,這一點與ES5完全不同冗栗。

newFoo();// ReferenceError

class Foo{}

上面代碼中,F(xiàn)oo類使用在前,定義在后贞瞒,這樣會報錯偶房,因為ES6不會把類的聲明提升到代碼頭部趁曼。這種規(guī)定的原因與下文要提到的繼承有關军浆,必須保證子類在父類之后定義。

Class的繼承

Class之間可以通過extends關鍵字實現(xiàn)繼承挡闰,這比ES5的通過修改原型鏈實現(xiàn)繼承乒融,要清晰和方便很多。

class ColorPoint extends Point{}

上面代碼定義了一個ColorPoint類摄悯,該類通過extends關鍵字赞季,繼承了Point類的所有屬性和方法。但是由于沒有部署任何代碼奢驯,所以這兩個類完全一樣申钩,等于復制了一個Point類。下面瘪阁,我們在ColorPoint內(nèi)部加上代碼撒遣。

class ColorPoint extends Point{

constructor(x,y,color){

super(x,y);//調(diào)用父類的constructor(x, y)

this.color=color;

}

toString(){

returnthis.color+' '+super.toString();//調(diào)用父類的toString()

}? ? }

上面代碼中,constructor方法和toString方法之中管跺,都出現(xiàn)了super關鍵字义黎,它在這里表示父類的構造函數(shù),用來新建父類的this對象豁跑。


子類必須在constructor方法中調(diào)用super方法廉涕,否則新建實例時會報錯。這是因為子類沒有自己的this對象艇拍,而是繼承父類的this對象狐蜕,然后對其進行加工。如果不調(diào)用super方法卸夕,子類就得不到this對象馏鹤。

class Point{/* ... */}

class ColorPoint extends Point{

constructor(){

}

}

letcp=newColorPoint();// ReferenceError


上面代碼中,ColorPoint繼承了父類Point娇哆,但是它的構造函數(shù)沒有調(diào)用super方法湃累,導致新建實例時報錯。

ES5的繼承碍讨,實質(zhì)是先創(chuàng)造子類的實例對象this治力,然后再將父類的方法添加到this上面(Parent.apply(this))。ES6的繼承機制完全不同勃黍,實質(zhì)是先創(chuàng)造父類的實例對象this(所以必須先調(diào)用super方法)宵统,然后再用子類的構造函數(shù)修改this。

如果子類沒有定義constructor方法,這個方法會被默認添加马澈,代碼如下瓢省。也就是說,不管有沒有顯式定義痊班,任何一個子類都有constructor方法勤婚。

constructor(...args){

super(...args);

}

另一個需要注意的地方是,在子類的構造函數(shù)中涤伐,只有調(diào)用super之后馒胆,才可以使用this關鍵字,否則會報錯凝果。這是因為子類實例的構建祝迂,是基于對父類實例加工,只有super方法才能返回父類實例器净。

class Point{

constructor(x,y){

this.x=x;

this.y=y;

}

}

class ColorPoint extends Point{

constructor(x,y,color){

this.color=color;// ReferenceError

super(x,y);

this.color=color;//正確

}

}

上面代碼中型雳,子類的constructor方法沒有調(diào)用super之前,就使用this關鍵字山害,結(jié)果報錯纠俭,而放在super方法之后就是正確的。

下面是生成子類實例的代碼粗恢。

letcp=newColorPoint(25,8,'green');

cpinstanceofColorPoint// true

cpinstanceofPoint// true

上面代碼中柑晒,實例對象cp同時是ColorPoint和Point兩個類的實例,這與ES5的行為完全一致眷射。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末匙赞,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子妖碉,更是在濱河造成了極大的恐慌涌庭,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欧宜,死亡現(xiàn)場離奇詭異坐榆,居然都是意外死亡,警方通過查閱死者的電腦和手機冗茸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門席镀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人夏漱,你說我怎么就攤上這事豪诲。” “怎么了挂绰?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵屎篱,是天一觀的道長。 經(jīng)常有香客問我,道長交播,這世上最難降的妖魔是什么重虑? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮秦士,結(jié)果婚禮上缺厉,老公的妹妹穿的比我還像新娘。我一直安慰自己伍宦,他們只是感情好芽死,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布乏梁。 她就那樣靜靜地躺著次洼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪遇骑。 梳的紋絲不亂的頭發(fā)上卖毁,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音落萎,去河邊找鬼亥啦。 笑死,一個胖子當著我的面吹牛练链,可吹牛的內(nèi)容都是我干的翔脱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼媒鼓,長吁一口氣:“原來是場噩夢啊……” “哼届吁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起绿鸣,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤疚沐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后潮模,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亮蛔,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年擎厢,在試婚紗的時候發(fā)現(xiàn)自己被綠了究流。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡动遭,死狀恐怖芬探,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沽损,我是刑警寧澤灯节,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響炎疆,放射性物質(zhì)發(fā)生泄漏卡骂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一形入、第九天 我趴在偏房一處隱蔽的房頂上張望全跨。 院中可真熱鬧,春花似錦亿遂、人聲如沸浓若。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挪钓。三九已至,卻和暖如春耳舅,著一層夾襖步出監(jiān)牢的瞬間碌上,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工浦徊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留馏予,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓盔性,卻偏偏與公主長得像霞丧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子冕香,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容