【JS】【進(jìn)階】常用的幾種繼承方案

繼承(英語:inheritance)是面向?qū)ο?/a>軟件技術(shù)當(dāng)中的一個概念。如果一個類別B“繼承自”另一個類別A泞遗,就把這個B稱為“A的子類”康吵,而把A稱為“B的父類別”也可以稱“A是B的超類”。繼承可以使得子類具有父類別的各種屬性和方法浙炼,而不需要再次編寫相同的代碼。在令子類別繼承父類別的同時唯袄,可以重新定義某些屬性弯屈,并重寫某些方法,即覆蓋父類別的原有屬性和方法恋拷,使其獲得與父類別不同的功能资厉。另外,為子類追加新的屬性和方法也是常見的做法蔬顾。 一般靜態(tài)的面向?qū)ο缶幊陶Z言宴偿,繼承屬于靜態(tài)的,意即在子類的行為在編譯期就已經(jīng)決定诀豁,無法在運(yùn)行期擴(kuò)展窄刘。

ES5中沒有繼承的,但是可以通過對象的原型修改構(gòu)造函數(shù)的指向來達(dá)到繼承的效果舷胜。

1. 原型鏈繼承

子類構(gòu)造函數(shù)的原型指向父類構(gòu)造函數(shù)的實例娩践,這樣,子類也擁有了父類的屬性和方法

基本實現(xiàn)

    function Father() {
        this.name = '我是爸爸'
    }
    Father.prototype.say = function () {
        console.log(this.name);
    }

    function Son () {
    }

    Son.prototype = new Father()

    var son = new Son()
    son.say() // 我是爸爸

缺點
子類實例會共享父類中相應(yīng)的值,當(dāng)一個子類修改了父類的屬性和方法(引用類型)欺矫,其他子類也相應(yīng)的父類屬性和方法也會被修改

    function Father () {
        this.sons = ['大明','二明','三明']
    }

    function Son () {

    }
    Son.prototype = new Father()

    var son1 = new Son()
    var son2 = new Son()

    // 當(dāng)son1改變了父類的屬性
    son1.sons.push('小明')
    console.log('son1', son1.sons)  // ["大明", "二明", "三明", "小明"]
    console.log('son2', son2.sons)  // ["大明", "二明", "三明", "小明"]

上面實例可以看出纱新,father有一個數(shù)組對象,當(dāng)子類構(gòu)造函數(shù)的prototype指向父類對象后穆趴,新建的子類對象就共享了父類father中的屬性和方法脸爱。當(dāng)son1對father中的引用類型進(jìn)行修改時,son2中的值也會對象的被修改

2. 構(gòu)造函數(shù)繼承

使用父類的構(gòu)造函數(shù)來增強(qiáng)子類實例未妹,等同于復(fù)制父類的實例給子類

核心代碼是Father.call(this)簿废,創(chuàng)建子類實例時調(diào)用Father構(gòu)造函數(shù),于是Son的每個實例都會將Father中的屬性和方法復(fù)制一份

基本實現(xiàn)

    function Father() {
        this.colors = ['red', 'black', 'green']
    }

    Father.prototype.name = 'Father'

    Father.prototype.say = function () {
        console.log('我是Father');
    }

    function Son() {
        Father.call(this)
    }

    var son = new Son()
    console.log(son);
結(jié)果

可以看出络它,在子類構(gòu)造函數(shù)中調(diào)用父類的方法族檬,可以將父類中的this指向子類實例,這樣子類實例就擁有了父類的屬性和方法化戳。
缺點:無法復(fù)制Father構(gòu)造函數(shù)原型的屬性和方法

3. 組合繼承

方法1方法2結(jié)合起來单料。組合繼承就是將原型鏈繼承和構(gòu)造函數(shù)繼承結(jié)合起來

  • 使用原型鏈繼承解決 父類構(gòu)造函數(shù)原型的屬性和方法繼承問題
  • 使用構(gòu)造函數(shù)繼承解決 父類實例中的屬性被共享的問題

基本實現(xiàn)

function Father() {
        this.colors = ['red', 'black', 'green']
     }

     Father.prototype.name = 'Father'
     Father.prototype.say = function() {
         console.log('我是Father')
     }

     function Son () {
         Father.call(this)
     }
     Son.prototype = new Father()


     var son1 = new Son()
     var son2 = new Son()

     // 修改一下son1
     son1.colors.push('pink')
     console.log('son1',son1);
     // 調(diào)用父類的原型方法
     son1.say()

     console.log('son2',son2);
     // 調(diào)用父類的原型方法
     son2.say()
結(jié)果
  1. 解決了修改父類實例中的變量其他子類實例也會被修改的問題
  2. 解決了prototype繼承的問題

缺點:可以看到,子類實例和子類的prototype對象中會有重復(fù)的部分

4. 原型式繼承

利用一個空對象作為中介点楼,將某個對象直接賦值給空對象構(gòu)造函數(shù)的原型

基本實現(xiàn)

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


    var person = {
        name: '劉德華',
        songs: ['冰雨','中國人','恭喜發(fā)財']
    }

    var son1 = object(person)
    son1.name = '張學(xué)友'
    son1.songs.push('李香蘭')

    var son2 = object(person)
    son2.name = '王菲'
    son2.songs.push('人間')

    console.log('son1',son1);
    console.log('son2',son2);
結(jié)果

上述代碼可以看到扫尖,son1和son2修改的是同一個songs對象
缺點:原型鏈繼承多個實例的引用類型屬性指向相同,存在篡改的可能

5. 寄生式繼承

在原型式繼承的基礎(chǔ)上掠廓,增強(qiáng)對象换怖,返回構(gòu)造函數(shù)

缺點:同原型式繼承相同

基本實現(xiàn)

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


    function createAnother(original) {
        var clone = object(original) // 通過調(diào)用object()函數(shù)創(chuàng)建一個新對象
        clone.sayHi = function () { // 以某種方式來增強(qiáng)對象
            alert('hi')
        }
        return clone // 返回這個對象
    }

    // 函數(shù)的主要作用是為構(gòu)造函數(shù)新增屬性和方法。以增強(qiáng)函數(shù)

    var person = {
        name: '劉德華',
        songs: ['冰雨']
    }

    var son1 = createAnother(person)
    var son2 = createAnother(person)
    son1.sayHi()
    son1.songs.push('李香蘭')
    son2.songs.push('人間')

    console.log('son1', son1);
    console.log('son2', son2);
結(jié)果

6. 寄生組合式繼承

一種比較完美的解決方案蟀瞧,解決了以上繼承的所有缺點

  1. 子類prototype指向父類原型對象
  2. 子類原型對象的contructor指向子類構(gòu)造函數(shù)
  3. 子類構(gòu)造函數(shù)中調(diào)用父類函數(shù)沉颂,復(fù)制父類函數(shù)的屬性和方法

基本實現(xiàn)

    function inheritPrototype(son, father) {
        // 復(fù)制一份父類的原型
        var prototype = Object.create(father.prototype)
        // 將原型對象上的構(gòu)造函數(shù)指向子類構(gòu)造函數(shù)
        prototype.constructor = son
        // 子類的原型對象指向父類原型對象
        son.prototype = prototype
    }

    function Father(name) {
        this.name = name
        this.colors = ['red', 'blue', 'green']
    }
    Father.prototype.tag = 'Father'
    Father.prototype.say = function () {
        alert('我是' + this.tag)
    }

    function Son(name, age) {
        Father.call(this, name)
        this.age = age
    }
    Son.prototype.sayAge = function () {
        alert('我今年' + this.age + '歲了')
    }

    inheritPrototype(Son, Father)

    var son1 = new Son('大明','23')
    var son2 = new Son('小明','20')

    son1.colors.push('black')



    console.log(son1);
    console.log(son2);
結(jié)果

7. 混入方式繼承多個對象

原理同寄生組合式繼承:將多個父類對象的prototype的屬性和方法復(fù)制到子類對象的prototype上

在構(gòu)造函數(shù)中,使用call方法悦污,調(diào)用父類對象铸屉,將多個父類對象的屬性和方法復(fù)制到子類對象上

基本實現(xiàn)


    function Father1() {

    }

    function Father2 () {

    }

    function Son () {
        Father1.call(this)
        Father2.call(this)
    }

    Son.prototype = Object.create(Father1.prototype)

    Object.assign(Son.prototype, Father2.prototype)

8. ES6 Class extends

核心: ES6繼承的結(jié)果和寄生組合繼承相似,本質(zhì)上塞关,ES6繼承是一種語法糖抬探。但是子巾,寄生組合繼承是先創(chuàng)建子類實例this對象帆赢,然后再對其增強(qiáng);而ES6先將父類實例對象的屬性和方法线梗,加到this上面(所以必須先調(diào)用super方法)椰于,然后再用子類的構(gòu)造函數(shù)修改this。

class A {}

class B extends A {
  constructor() {
    super();
  }
}

ES6實現(xiàn)繼承的具體原理:

class A {
}

class B {
}

Object.setPrototypeOf = function (obj, proto) {
  obj.__proto__ = proto;
  return obj;
}

// B 的實例繼承 A 的實例
Object.setPrototypeOf(B.prototype, A.prototype);

// B 繼承 A 的靜態(tài)屬性
Object.setPrototypeOf(B, A);

ES6繼承與ES5繼承的異同:

  • 相同點:本質(zhì)上ES6繼承是ES5繼承的語法糖
  • 不同點:
    1. ES5的繼承實質(zhì)上是先創(chuàng)建子類的實例對象仪搔,然后再將父類的方法添加到this上(Parent.call(this))
    2. ES6的繼承有所不同瘾婿,實質(zhì)上是先創(chuàng)建父類的實例對象this,然后再用子類的構(gòu)造函數(shù)修改this。因為子類沒有自己的this對象偏陪,所以必須先調(diào)用父類的super()方法抢呆,否則新建實例報錯。
  • 序言:七十年代末笛谦,一起剝皮案震驚了整個濱河市抱虐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌饥脑,老刑警劉巖恳邀,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異灶轰,居然都是意外死亡谣沸,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門笋颤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乳附,“玉大人,你說我怎么就攤上這事伴澄⌒斫Γ” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵秉版,是天一觀的道長贤重。 經(jīng)常有香客問我,道長清焕,這世上最難降的妖魔是什么并蝗? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮秸妥,結(jié)果婚禮上滚停,老公的妹妹穿的比我還像新娘。我一直安慰自己粥惧,他們只是感情好键畴,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著突雪,像睡著了一般起惕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咏删,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天惹想,我揣著相機(jī)與錄音,去河邊找鬼督函。 笑死嘀粱,一個胖子當(dāng)著我的面吹牛激挪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播锋叨,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼垄分,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了娃磺?” 一聲冷哼從身側(cè)響起锋喜,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎豌鸡,沒想到半個月后嘿般,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涯冠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年炉奴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛇更。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡瞻赶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出派任,到底是詐尸還是另有隱情砸逊,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布掌逛,位于F島的核電站师逸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏豆混。R本人自食惡果不足惜篓像,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望皿伺。 院中可真熱鬧员辩,春花似錦、人聲如沸鸵鸥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妒穴。三九已至宋税,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宰翅,已是汗流浹背弃甥。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工爽室, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留汁讼,地道東北人淆攻。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像嘿架,于是被迫代替她去往敵國和親瓶珊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353