JavaScript繼承

前言

?在面向?qū)ο缶幊讨衅蚯桑瑢?duì)象的繼承是很重要的一個(gè)概念涨椒。A 對(duì)象通過(guò)繼承 B 對(duì)象,就能直接擁有 B 對(duì)象的所有屬性和方法绽媒。這對(duì)于代碼的復(fù)用是非常有用的蚕冬。
現(xiàn)在梳理下js的繼承

方式一、原型鏈繼承

實(shí)現(xiàn)核心

?這個(gè)繼承方法的核心是:將父類的實(shí)例作為子類的原型

// 父類
function Parent(name) {
    this.name = name;
    this.play = [1, 2, 3];
    this.setName = function (name) { this.name = name };
}

Parent.prototype.getName = function () {
    console.log("parent name:", this.name);
}
// 子類
function Child(name) {
    this.name = name; 
}
// 核心: 將父類的實(shí)例作為子類的原型
Child.prototype = new Parent("father");
Child.prototype.constructor=Child是辕;//需手動(dòng)綁定constructor
var child1 = new Child("son1");
child1.getName();//parent name: son1
child1.setName("ganin");
child1.getName();//parent name: ganin
var child2 = new Child("son2");
child2.getName();//parent name: son2

?這種方法實(shí)際是將子類的原型指向父類的實(shí)例囤热,所以子類還是可以通過(guò)getPrototypeOf訪問(wèn)到Parent的實(shí)例的,這樣就可以訪問(wèn)的父類的私有方法了免糕,然后在通過(guò)getPrototypeOf就可以獲得父類原型上的方法了赢乓。簡(jiǎn)單來(lái)說(shuō)就是子類繼承父類的屬性和方法是將父類的私有屬性和公有方法都作為自己的公有屬性和方法忧侧。這樣也有一個(gè)不足之處石窑,如果說(shuō)父類的私有屬性中有引用類型的屬性,那它被子類繼承的時(shí)候會(huì)作為公有屬性蚓炬,這樣子類1操作這個(gè)屬性的時(shí)候松逊,就會(huì)影響到子類2。見(jiàn)例子:

console.log(child2.play)//[ 1, 2, 3 ]
child1.play.push(4)
console.log(child2.play)//[ 1, 2, 3, 4 ]

特點(diǎn)

??父類新增原型方法/原型屬性肯夏,子類都能訪問(wèn)到
??簡(jiǎn)單经宏,易于實(shí)現(xiàn)

缺點(diǎn)

?無(wú)法實(shí)現(xiàn)多繼承
?不能定義私有屬性方法
?沒(méi)辦法向父類傳遞參數(shù)
?無(wú)論是定義還是繼承都需要手動(dòng)修改 constructor
?要想為子類新增屬性和方法,必須要在Student.prototype = new Person() 之后執(zhí)行驯击,不能放到構(gòu)造器中烁兰。

方式二、構(gòu)造函數(shù)繼承(類式繼承)

實(shí)現(xiàn)核心:在子類型構(gòu)造函數(shù)中使用call()調(diào)用父類型構(gòu)造函數(shù)

// 父類
function Parent(name,age){
    this.name=name;
    this.age=age;
    this.play = [1, 2, 3];
    this.setName = function (name) { this.name = name };
}
Parent.prototype.getName = function () {
    console.log("parent name:", this.name);
}
function Child(name,age){
    Parent.call(this,name,age);
}
var child1=new Child("agam",20);
var child2=new Child("tom",20);
console.log(child1.name)//agam
child1.setName("agamgn")
console.log(child1.name)//agamgn
console.log(child2.name)//tom
child1.getName();//child1.getName is not a function

?這種方式只是實(shí)現(xiàn)部分的繼承徊都,如果父類的原型還有方法和屬性沪斟,子類是拿不到這些方法和屬性的。

特點(diǎn)

??可以定義私有屬性方法暇矫,解決了原型鏈繼承中子類實(shí)例共享父類引用屬性的問(wèn)題
??子類可以向父類傳遞參數(shù)
??可以實(shí)現(xiàn)多繼承(call多個(gè)父類對(duì)象)

缺點(diǎn)

?實(shí)例并不是父類的實(shí)例主之,只是子類的實(shí)例
?只能繼承父類的實(shí)例屬性和方法,不能繼承原型屬性和方法
?無(wú)法實(shí)現(xiàn)函數(shù)復(fù)用李根,每個(gè)子類都有父類實(shí)例函數(shù)的副本槽奕,影響性能

方式三、組合繼承(原型鏈+構(gòu)造函數(shù))

實(shí)現(xiàn)核心:通過(guò)調(diào)用父類構(gòu)造房轿,繼承父類的屬性并保留傳參的優(yōu)點(diǎn)粤攒,然后通過(guò)將父類實(shí)例作為子類原型所森,實(shí)現(xiàn)函數(shù)復(fù)用。

// 父類
function Parent(name,age){
    this.name=name;
    this.age=age;
    this.play = [1, 2, 3];
    this.setName = function (name) { this.name = name };
}
Parent.prototype.getName = function () {
    console.log("parent name:", this.name);
}
function Child(name,age){
    Parent.call(this,name,age);
}

Child.prototype=new Parent();
Child.prototype.constructor=Child;//組合繼承也是需要修復(fù)構(gòu)造函數(shù)指向的
Child.prototype.satHello=function(){console.log("hello")}
var child1=new Child("agam",18);
var child2=new Child("agamgn",18);
console.log(child1)//Child { name: 'agam',
 age: 18, play: [ 1, 2, 3 ], setName: [Function] }
child1.setName("Tom")
console.log(child1)//Child { name: 'Tom', 
age: 18, play: [ 1, 2, 3 ], setName: [Function] }
console.log(child2)//Child {name: 'agamgn',
age: 18, play: [ 1, 2, 3 ],setName: [Function] }

?這種方式是 JavaScript 中最常用的繼承模式夯接。不過(guò)也存在缺點(diǎn)就是無(wú)論在什么情況下必峰,都會(huì)調(diào)用兩次構(gòu)造函數(shù):一次是在創(chuàng)建子類型原型的時(shí)候,另一次是在子類型構(gòu)造函數(shù)的內(nèi)部钻蹬,子類型最終會(huì)包含父類型對(duì)象的全部實(shí)例屬性吼蚁,但我們不得不在調(diào)用子類構(gòu)造函數(shù)時(shí)重寫(xiě)這些屬性。

特點(diǎn)

??可以繼承實(shí)例屬性/方法问欠,也可以繼承原型屬性/方法
??公有的寫(xiě)在原型肝匆,私有的寫(xiě)在構(gòu)造函數(shù)
??可以向父類傳遞參數(shù)
??函數(shù)可復(fù)用

缺點(diǎn)

?調(diào)用了兩次父類構(gòu)造函數(shù),生成了兩份實(shí)例(子類實(shí)例將子類原型上的那份屏蔽了)
? 需要手動(dòng)綁定 constructor

方式四顺献、組合繼承優(yōu)化1

實(shí)現(xiàn)核心:通過(guò)父類原型和子類原型指向同一對(duì)象

// 父類
function Parent(name,age){
    this.name=name;
    this.age=age;
    this.play = [1, 2, 3];
    this.setName = function (name) { this.name = name };
}
Parent.prototype.getName = function () {
    console.log("parent name:", this.name);
}
function Child(name,age){
    Parent.call(this,name,age);
}

// 此處父類原型和子類原型指向同一對(duì)象
Child.prototype=Parent.prototype;
Child.prototype.satHello=function(){console.log("hello")}

var child1=new Child("agam",18);
var child2=new Child("agamgn",18);
console.log(child1)//Child { name: 'agam', age: 18, play: [ 1, 2, 3 ], setName: [Function] }
child1.setName("Tom")
console.log(child1)//Child { name: 'Tom', age: 18, play: [ 1, 2, 3 ], setName: [Function] }
console.log(child2)//Child {name: 'agamgn',age: 18, play: [ 1, 2, 3 ],setName: [Function] }

特點(diǎn)

??不會(huì)初始化兩次實(shí)例方法/屬性旗国,避免的組合繼承的缺點(diǎn)

缺點(diǎn)

?沒(méi)辦法辨別是實(shí)例是子類還是父類創(chuàng)造的,子類和父類的構(gòu)造函數(shù)指向是同一個(gè)

方式五注整、原型式繼承

實(shí)現(xiàn)核心:直接使用 ES5 Object.create 方法能曾,該方法的原理是創(chuàng)建一個(gè)構(gòu)造函數(shù),構(gòu)造函數(shù)的原型指向?qū)ο笾坠欤缓笳{(diào)用 new 操作符創(chuàng)建實(shí)例寿冕,并返回這個(gè)實(shí)例,本質(zhì)是一個(gè)淺拷貝椒袍。

// 父類
function Parent(name,age){
    this.name=name;
    this.age=age;
    this.play = [1, 2, 3];
    this.setName = function (name) { this.name = name };
}
Parent.prototype.getName = function () {
    console.log("parent name:", this.name);
}
function Child(name,age){
    Parent.call(this,name,age);
}

// 核心代碼
Child.prototype=Object.create(Parent.prototype);
Child.prototype.constructor=Child;//核心代碼
var child1=new Child("agam",18);
var child2=new Child("agamgn",18);
console.log(child1)//Child { name: 'agam', age: 18, play: [ 1, 2, 3 ], setName: [Function] }
child1.setName("Tom")
console.log(child1)//Child { name: 'Tom', age: 18, play: [ 1, 2, 3 ], setName: [Function] }
console.log(child2)//Child {name: 'agamgn',age: 18, play: [ 1, 2, 3 ],setName: [Function] }

特點(diǎn)

??父類方法可以復(fù)用

缺點(diǎn)

?父類引用屬性全部被共享
?子類不可傳遞參數(shù)給父類

方式六驼唱、ES6 class

實(shí)現(xiàn)核心:ES6中引入了class關(guān)鍵字,是一種語(yǔ)法糖驹暑。

class Parent{
    constructor(name){
        this.name=name;
    }
    getName(){
        console.log(this.name);
    }
}

let p=new Parent("ganin");
console.log(p);//Parent { name: 'ganin' }

class Child extends Parent{
    constructor(name,age){
        super(name);//通過(guò)supper調(diào)用父類的構(gòu)造方法
        this.age=age;
    }
    get(){
        console.log(this.name,this.age)
    }
}
let s=new Child("tom",18);
console.log(s)//Child { name: 'tom', age: 18 }

特點(diǎn)

??語(yǔ)法簡(jiǎn)單易懂,操作更方便

缺點(diǎn)

?并不是所有的瀏覽器都支持class關(guān)鍵字

總結(jié)

參考一篇文章理解JS繼承——原型鏈/構(gòu)造函數(shù)/組合/原型式/寄生式/寄生組合/Class extends
本章節(jié)代碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末玫恳,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子优俘,更是在濱河造成了極大的恐慌京办,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帆焕,死亡現(xiàn)場(chǎng)離奇詭異惭婿,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)视搏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門审孽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人浑娜,你說(shuō)我怎么就攤上這事佑力。” “怎么了筋遭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵打颤,是天一觀的道長(zhǎng)暴拄。 經(jīng)常有香客問(wèn)我,道長(zhǎng)编饺,這世上最難降的妖魔是什么乖篷? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮透且,結(jié)果婚禮上撕蔼,老公的妹妹穿的比我還像新娘。我一直安慰自己秽誊,他們只是感情好鲸沮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著锅论,像睡著了一般讼溺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上最易,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天怒坯,我揣著相機(jī)與錄音,去河邊找鬼藻懒。 笑死剔猿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的束析。 我是一名探鬼主播艳馒,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼憎亚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼员寇!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起第美,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蝶锋,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后什往,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體扳缕,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年别威,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了躯舔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡省古,死狀恐怖粥庄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情豺妓,我是刑警寧澤惜互,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布布讹,位于F島的核電站,受9級(jí)特大地震影響训堆,放射性物質(zhì)發(fā)生泄漏描验。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一坑鱼、第九天 我趴在偏房一處隱蔽的房頂上張望膘流。 院中可真熱鬧,春花似錦鲁沥、人聲如沸睡扬。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)卖怜。三九已至,卻和暖如春阐枣,著一層夾襖步出監(jiān)牢的瞬間马靠,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工蔼两, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留甩鳄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓额划,卻偏偏與公主長(zhǎng)得像妙啃,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子俊戳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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

  • JavaScript 繼承機(jī)制的設(shè)計(jì)思想就是揖赴,原型對(duì)象的所有屬性和方法,都能被實(shí)例對(duì)象共享抑胎。所以我們只要改變對(duì)象的...
    Kevin丶CK閱讀 280評(píng)論 0 1
  • 前言 也許學(xué)過(guò)JavaScript繼承方式的朋友都知道燥滑,自定義引用類型的最佳繼承方式非寄生組合繼承方式莫屬;然而阿逃,...
    阿爾卑斯的隆冬閱讀 642評(píng)論 0 1
  • 寫(xiě)出一個(gè)構(gòu)造函數(shù) Animal輸入:空輸出:一個(gè)新對(duì)象铭拧,該對(duì)象的共有屬性為 {行動(dòng): function(){}},...
    許驍Charles閱讀 331評(píng)論 0 2
  • 之前的JavaScript繼承一文中已經(jīng)介紹了繼承恃锉,但那篇只能算簡(jiǎn)介搀菩。本篇結(jié)合原型鏈詳細(xì)介紹一下JavaScrip...
    張歆琳閱讀 2,593評(píng)論 0 8
  • 開(kāi)篇詞,結(jié)束語(yǔ)破托,今天當(dāng)著眾目睽睽之下要娶你為妻
    zoQ閱讀 37評(píng)論 0 0