JavaScript繼承新舊方法匯總

例子

我們生成兩個構(gòu)造函數(shù),后面的例子都是讓‘’貓‘’繼承‘’動物‘’的所有屬性和方法关划。

  • 動物(為了更好的理解各種繼承,這里給動物附上了基本類型和引用類型)
function Animal() {    
    this.species = "動物"
    this.do = ['運(yùn)動', '繁殖'] 
}
function Cat(name, color) {    
    this.name = name   
    this.color = color
}

1.簡單的原型鏈

這可能是最簡單直觀的一種實(shí)現(xiàn)繼承方式了

1.1 實(shí)現(xiàn)方法

function Animal() {    
    this.species = "動物"
    this.do = ['運(yùn)動', '繁殖'] 
}  
function Cat(name, color) {    
    this.name = name   
    this.color = color
}
Cat.prototype = new Animal //重點(diǎn)G涛汀V邸!W手选调榄!
Cat.prototype.constructor = Cat
var cat1 = new Cat('小黃', '黃色')
console.log(cat1.species) // 動物
console.log(cat1.do) // [ '運(yùn)動', '繁殖' ] 

1.2 核心

這種方法的核心就這一句話:Cat.prototype = new Animal 也就是拿父類實(shí)例來充當(dāng)子類原型對象

1.3 優(yōu)缺點(diǎn)

然而這個方法雖然簡單但是有一個很嚴(yán)重的問題:在我們修改一個實(shí)例的屬性時,其他的也隨之改變呵扛。

var cat1 = new Cat('小黃', '黃色')
var cat2 = new Cat('小白', '白色')
cat1.species = '哺乳動物'
cat1.do.push('呼吸')
console.log(cat1.species) // 哺乳動物
console.log(cat2.species) // 動物
console.log(cat1.do) // [ '運(yùn)動', '繁殖', '呼吸' ]
console.log(cat2.do) // [ '運(yùn)動', '繁殖', '呼吸' ]
  • 優(yōu)點(diǎn)
  1. 容易實(shí)現(xiàn)
  • 缺點(diǎn)
    1. 修改cat1.do后cat2.do也變了每庆,因?yàn)閬碜栽蛯ο蟮囊脤傩允撬袑?shí)例共享的。
      可以這樣理解:執(zhí)行cat1.do.push('呼吸');先對cat1進(jìn)行屬性查找今穿,找遍了實(shí)例屬性(在本例中沒有實(shí)例屬性)缤灵,沒找到,就開始順著原型鏈向上找,拿到了cat1的原型對象腮出,一搜身帖鸦,發(fā)現(xiàn)有do屬性。于是給do末尾插入了'呼吸'胚嘲,所以sub2.do也變了

    2. 創(chuàng)建子類實(shí)例時富蓄,無法向父類構(gòu)造函數(shù)傳參

1.4 繼承鏈的紊亂問題

Cat.prototype = new Animal

任何一個prototype對象都有一個constructor屬性,指向它的構(gòu)造函數(shù)慢逾。如果沒有"Cat.prototype = new Animal();"這一行立倍,Cat.prototype.constructor是指向Cat的。加了這一行以后侣滩,Cat.prototype.constructor指向Animal口注。

alert(Cat.prototype.constructor == Animal) //true

更重要的是,每一個實(shí)例也有一個constructor屬性君珠,默認(rèn)調(diào)用prototype對象的constructor屬性寝志。因此,在運(yùn)行"Cat.prototype = new Animal();"這一行之后策添,cat1.constructor也指向Animal材部!

alert(cat1.constructor == Cat.prototype.constructor) // true

這顯然會導(dǎo)致繼承鏈的紊亂(cat1明明是用構(gòu)造函數(shù)Cat生成的),因此我們必須手動糾正唯竹,將Cat.prototype對象的constructor值改為Cat乐导。

Cat.prototype.constructor = Cat

這是很重要的一點(diǎn),編程時務(wù)必要遵守浸颓。下文都遵循這一點(diǎn)物臂,即如果替換了prototype對象,那么产上,下一步必然是為新的prototype對象加上constructor屬性棵磷,并將這個屬性指回原來的構(gòu)造函數(shù)。

2. 借用構(gòu)造函數(shù)

使用call或apply方法晋涣,將父對象的構(gòu)造函數(shù)綁定在子對象上仪媒,即在子對象構(gòu)造函數(shù)中加一行:Animal.apply(this, arguments)

2.1 實(shí)現(xiàn)方法

function Animal() {    
    this.species = "動物"
    this.do = ['運(yùn)動', '繁殖'] 
}  
function Cat(name, color) {  
    Animal.call(this, arguments) ///重點(diǎn)!P蝗怠K惴浴!
    this.name = name   
    this.color = color
}
var cat1 = new Cat('小黃', '黃色')
console.log(cat1.species) // 動物
console.log(cat1.do) // [ '運(yùn)動', '繁殖' ] 

2.2 核心

借父類的構(gòu)造函數(shù)來增強(qiáng)子類實(shí)例撇贺,等于是把父類的實(shí)例屬性復(fù)制了一份給子類實(shí)例裝上了(完全沒有用到原型)

2.3 優(yōu)缺點(diǎn)

var cat1 = new Cat('小黃', '黃色')
var cat2 = new Cat('小白', '白色')
cat1.species = '哺乳動物'
cat1.do.push('呼吸')
console.log(cat1.species) // 哺乳動物
console.log(cat2.species) // 動物
console.log(cat1.do) // [ '運(yùn)動', '繁殖', '呼吸' ]
console.log(cat2.do) // [ '運(yùn)動', '繁殖' ]
  • 優(yōu)點(diǎn):
  1. 解決了子類實(shí)例共享父類引用屬性的問題
  2. 創(chuàng)建子類實(shí)例時赌莺,可以向父類構(gòu)造函數(shù)傳參
  • 缺點(diǎn):
  1. 無法實(shí)現(xiàn)函數(shù)復(fù)用冰抢,過多的占用內(nèi)存松嘶。
  2. 創(chuàng)建子類實(shí)例時,無法向父類構(gòu)造函數(shù)傳參

3. 組合繼承(偽經(jīng)典繼承)

將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合起來挎扰,發(fā)揮二者之長:使用原型鏈實(shí)現(xiàn)對原型屬性和方法的繼承翠订,而通過借用構(gòu)造函數(shù)來實(shí)現(xiàn)對實(shí)例屬性的繼承巢音。這樣,既通過在原型上定義的方法實(shí)現(xiàn)了函數(shù)復(fù)用尽超,又能夠保證每個實(shí)例都有它自己的屬性官撼。是實(shí)現(xiàn)繼承最常用的方式。

3.1 實(shí)現(xiàn)方法

function Animal() {    
    this.species = "動物"
    this.do = ['運(yùn)動', '繁殖'] 
}  
function Cat(name, color) {  
    Animal.call(this, arguments)//重點(diǎn)K扑0列濉!巩踏!
    this.name = name   
    this.color = color
}
Cat.prototype = new Animal//重點(diǎn)M核小!H怼菠净!
Cat.prototype.constructor = Cat
var cat1 = new Cat('小黃', '黃色')
console.log(cat1.species) // 動物
console.log(cat1.do) // [ '運(yùn)動', '繁殖' ] 

3.2 核心

把實(shí)例函數(shù)都放在原型對象上,以實(shí)現(xiàn)函數(shù)復(fù)用彪杉。同時還要保留借用構(gòu)造函數(shù)方式的優(yōu)點(diǎn)毅往,通過Animal.call(this);繼承父類的基本屬性和引用屬性并保留能傳參的優(yōu)點(diǎn);通過Cat.prototype = new Animal繼承父類函數(shù)派近,實(shí)現(xiàn)函數(shù)復(fù)用攀唯。

3.3 優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):
  1. 不存在引用屬性共享問題
  2. 可傳參
  3. 函數(shù)可復(fù)用
  • 缺點(diǎn):
  1. 子類原型上有一份多余的父類實(shí)例屬性,因?yàn)楦割悩?gòu)造函數(shù)被調(diào)用了兩次渴丸,生成了兩份革答。(私有屬性一份,原型里面一份)

4. 原型式

道格拉斯·克羅克福德在2006年寫了一篇文章曙强,Prototypal Inheritance in JavaScript(JavaScript中的原型式繼承)残拐。在這篇文章中,他介紹了一種實(shí)現(xiàn)繼承的方法碟嘴,這種方法并沒有使用嚴(yán)格意義上的構(gòu)造函數(shù)溪食。他的想法是借助原型可以基于已有的對象創(chuàng)建新對象,同時還不必因此創(chuàng)建自定義類型娜扇,為了達(dá)到這個目的错沃,他給出了如下函數(shù)。

function object(o) {
    function F() {}
    f.prototype = o
    return new F()
}

在object()函數(shù)內(nèi)部雀瓢,先創(chuàng)建了一個臨時性的構(gòu)造函數(shù)枢析,然后將傳入的對象作為這個構(gòu)造函數(shù)的原型,最后返回了這個臨時類型的一個新實(shí)例刃麸。從本質(zhì)上講醒叁,object()對傳入其中的對象執(zhí)行了一次淺拷貝。

4.1 實(shí)現(xiàn)方法

function object(o) {
    function F() {}
    F.prototype = o
    return new F()
}

function Animal() {    
    this.species = "動物"
    this.do = ['運(yùn)動', '繁殖'] 
} 
var Animal1 = new Animal 
var cat1 = object(Animal1)//重點(diǎn)!0颜印0∫住!

cat1.name = '小黃'
cat1.color = '黃色'

console.log(cat1.species) //動物
console.log(cat1.do) //["運(yùn)動", "繁殖"]

4.2 核心

核心就是通過一個函數(shù)來得到一個空的新對象饮睬,再在空對象的基礎(chǔ)上添加需要的方法(實(shí)例屬性)

4.3 優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):
  1. 從已有對象衍生新對象租谈,不需要創(chuàng)建自定義類型。
  • 缺點(diǎn):
  1. 原型引用屬性會被所有實(shí)例共享捆愁,因?yàn)槭怯谜麄€父類對象來充當(dāng)了子類
    原型對象割去,所以這個缺陷無可避免
  2. 無法實(shí)現(xiàn)代碼復(fù)用

5. 寄生式

寄生式在我看來和原型式差別不大,只是把對空對象私有屬性的添加封裝成了一個函數(shù)昼丑。

5.1 實(shí)現(xiàn)方法

function object(o) {
    function F() {}
    F.prototype = o
    return new F()
}

function Animal() {    
    this.species = "動物"
    this.do = ['運(yùn)動', '繁殖'] 
} 
function getCatObject(obj) {
    var clone = object(obj)//重點(diǎn)=俎帧!7恕页慷!
    clone.name = '小黃'
    clone.color = '黃色'
    return clone
}

var cat1 = getCatObject(new Animal)

console.log(cat1.species) //動物
console.log(cat1.do) //["運(yùn)動", "繁殖"]

5.2 核心

只是給原型式繼承套了一個殼子而已。
對于寄生式的理解:創(chuàng)建新對象 -> 增強(qiáng) -> 返回該對象胁附,這樣的過程叫寄生式繼承酒繁,新對象是如何創(chuàng)建的并不重要。

5.3 優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):
  1. 不需要創(chuàng)建自定義類型控妻。
  • 缺點(diǎn):
  1. 無法實(shí)現(xiàn)代碼復(fù)用

6. 寄生組合繼承

前面說過州袒,組合繼承是JavaScript 最常用的繼承模式;不過弓候,它也有自己的不足郎哭。組合繼承最大的問題就是無論什么情況下,都會調(diào)用兩次超類型構(gòu)造函數(shù):一次是在創(chuàng)建子類型原型的時候菇存,另一次是在子類型構(gòu)造函數(shù)內(nèi)部夸研。也就是會出現(xiàn)這種情況:



我們發(fā)現(xiàn)在私有屬性和原型里面都有name和do的屬性,這是因?yàn)檎{(diào)用了兩次構(gòu)造函數(shù)造成的后果依鸥,這必然會過多占用內(nèi)存亥至。
寄生組合繼承完美的解決了這個問題。

6.1 實(shí)現(xiàn)方法

function object(o) {
    function F() {}
    F.prototype = o
    return new F()
}

function Animal() {    
    this.species = "動物"
    this.do = ['運(yùn)動', '繁殖'] 
} 
function Cat(name, color) {
    Animal.call(this, arguments)//重點(diǎn)<佟=惆纭!衣吠!
    this.name = name
    this.color = color
}


var proto = object(Animal.prototype)//重點(diǎn)2杳簟!8壳巍惊搏!
proto.constructor = Cat//重點(diǎn)V椤!U陀臁!
Cat.prototype = proto//重點(diǎn)0浮K拚浮!才写!

var cat1 = new Cat()

console.log(cat1.species) //動物
console.log(cat1.do) //["運(yùn)動", "繁殖"]

6.2 核心

用object(Animal.prototype)切掉了原型對象上多余的那份父類實(shí)例屬性

6.3 優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):
  1. 幾乎完美
  • 缺點(diǎn):
  1. 用起來有些麻煩葡兑,理論上沒有缺點(diǎn)。

7. ES5使用 Object.create 創(chuàng)建對象

ECMAScript 5 中引入了一個新方法:Object.create()
赞草《锏蹋可以調(diào)用這個方法來創(chuàng)建一個新對象。新對象的原型就是調(diào)用 create方法時傳入的第一個參數(shù):

var a = {a: 1}; 
// a ---> Object.prototype ---> null

var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (繼承而來)

var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null

var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty); // undefined, 因?yàn)閐沒有繼承Object.prototype

8. ES6使用 class 關(guān)鍵字

ECMAScript6 引入了一套新的關(guān)鍵字用來實(shí)現(xiàn) class厨疙。使用基于類語言的開發(fā)人員會對這些結(jié)構(gòu)感到熟悉洲守,但它們是不一樣的。 JavaScript 仍然是基于原型的沾凄。這些新的關(guān)鍵字包括 class
, constructor
, static
, extends
, 和 super
.
例子如下:

class Animal {
    constructor(species, canDo) {
        this.species = '動物'
        this.canDo = ['運(yùn)動', '繁殖'] 
    }
}

class Cat extends Animal {
    constructor(name, color) {
        super()
        this.name = name
        this.color = color
    }
}
var cat1 = new Cat('小黃', '黃色')
console.dir(cat1)

9. 參考文獻(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末梗醇,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子撒蟀,更是在濱河造成了極大的恐慌叙谨,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件保屯,死亡現(xiàn)場離奇詭異手负,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)姑尺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門竟终,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人切蟋,你說我怎么就攤上這事衡楞。” “怎么了敦姻?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵瘾境,是天一觀的道長。 經(jīng)常有香客問我镰惦,道長迷守,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任旺入,我火速辦了婚禮兑凿,結(jié)果婚禮上凯力,老公的妹妹穿的比我還像新娘。我一直安慰自己礼华,他們只是感情好咐鹤,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著圣絮,像睡著了一般祈惶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扮匠,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天捧请,我揣著相機(jī)與錄音,去河邊找鬼棒搜。 笑死疹蛉,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的力麸。 我是一名探鬼主播可款,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼克蚂!你這毒婦竟也來了筑舅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤陨舱,失蹤者是張志新(化名)和其女友劉穎翠拣,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體游盲,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡误墓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了益缎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谜慌。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖莺奔,靈堂內(nèi)的尸體忽然破棺而出欣范,到底是詐尸還是另有隱情,我是刑警寧澤令哟,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布恼琼,位于F島的核電站,受9級特大地震影響屏富,放射性物質(zhì)發(fā)生泄漏晴竞。R本人自食惡果不足惜狠半,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一颤难、第九天 我趴在偏房一處隱蔽的房頂上張望已维。 院中可真熱鬧行嗤,春花似錦、人聲如沸垛耳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽占婉。三九已至泡嘴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逆济,已是汗流浹背酌予。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抛虫,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓建椰,卻偏偏與公主長得像岛马,于是被迫代替她去往敵國和親棉姐。 傳聞我的和親對象是個殘疾皇子啦逆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

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