JS繼承詳解

1.原型鏈繼承

?原型鏈繼承所帶來的問題:

??① 引用類型的屬性被所有實例共享。
??② 在創(chuàng)建 Child 的實例時澈驼,不能向Parent傳參

例子:

function Parent (age) {
   this.names = ['kevin', 'daisy'];
   this.age = age;
}
function Child () {
}
Child.prototype = new Parent();
var child1 = new Child('age');
child1.names.push('yayu');
console.log(child1.names); // ["kevin", "daisy", "yayu"]
console.log(child1.age);// undefined
var child2 = new Child();
console.log(child2.names); // ["kevin", "daisy", "yayu"]

2.構造函數(shù)(經典)繼承

?構造函數(shù)(經典)繼承的優(yōu)點:

??① 避免了引用類型的屬性被所有實例共享。
??② 可以在 Child 中向 Parent 傳參

?缺點:

??① 無法繼承原型鏈上的屬性和方法。

例子:

function Parent (name) {
    this.name = name;
    this.age = ['10','12'];
        //a方法定義在構造函數(shù)
    this.a = function(){
        console.log("a");
    }
}
//方法定義在原型鏈
Parent.prototype.b = function(){
    console.log("b");
}
function Child (name) {
    Parent.call(this, name); //或者用.apply()方法
}
var child1 = new Child('kevin'); 
child1.age.push('16');
console.log(child1.name); // kevin
console.log(child1.age); //['10','12','16']
console.log(child1.a()); //a
console.log(child1.b()); //報錯:child1.b is not a function;即無法繼承原型鏈上的屬性和方法

var child2 = new Child('daisy');
child2.age.push('22');
console.log(child2.name); // daisy
console.log(child2.age); //['10','12','22']

3.組合繼承(原型鏈繼承和經典繼承融合起來)

?組合繼承的優(yōu)點:

??① 彌補了上述兩種方法存在的問題

?缺點:

??① 會調用兩次父構造函數(shù)敏晤。一次是設置子類型實例的原型的時候;一次在創(chuàng)建子類型實例的時候。

例子:

function Parent (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
    console.log(this.name)
}
function Child (name, age) {
    Parent.call(this, name);  //注意這里的坑缅茉,在這里嘴脾,我們又會調用了一次 Parent 構造函數(shù)。如果我們打印 child1 對象蔬墩,我們會發(fā)現(xiàn) Child.prototype 和 child1 都有一個屬性為colors译打,屬性值為[‘red’, ‘blue’,‘green’]。
    this.age = age;
}
Child.prototype = new Parent(); //父類的構造函數(shù)是被執(zhí)行了兩次的拇颅,第一次:Child.prototype = new Parent();第二次:實例化的時候會被執(zhí)行;
//優(yōu)化(就變成了后面說的寄生組合繼承):
//Child.prototype = Object.create(Parent.prototype); 
/*Object.create方法是一種創(chuàng)建對象的方式 
  1.方法內部定義一個新的空對象obj 
  2.將obj._proto _的對象指向傳入的參數(shù)proto 
  3.返回一個新的對象 奏司;
Object.create方法底層實現(xiàn)實際上用到了之后我們將要說的原型式方法,關于Object.create方法更詳細的介紹可以自己查資料;
*/
    /*Object.create的底層實現(xiàn)
    Object.create =  function (o) {
            var F = function () {};
            F.prototype = o;
            return new F();
    };*/
Child.prototype.constructor = Child; //注意這里的坑樟插,如果不加韵洋,console.log(child1.constructor.name) //Parent  實例化的時候構造函數(shù)指向的是父類
var child1 = new Child('kevin', '18'); //第二次調用父構造函數(shù)
child1.colors.push('black');
console.log(child1.name); // kevin
console.log(child1.age); // 18
console.log(child1.colors); // ["red", "blue", "green", "black"]
console.log(child1 instanceof Child) //true;
console.log(child1 instanceof Parent) //true;
//注意:
console.log(child1.constructor.name); //Child;不加這個Child.prototype.constructor = Child;打印結果是Parent

var child2 = new Child('daisy', '20');
console.log(child2.name); // daisy
console.log(child2.age); // 20
console.log(child2.colors); // ["red", "blue", "green"]

4.原型式繼承

?就是 ES5 Object.create 的模擬實現(xiàn)黄锤,將傳入的對象作為創(chuàng)建的對象的原型麻献。

?缺點:

??① 包含引用類型的屬性值始終都會共享相應的值,這點跟原型鏈繼承一樣猜扮。

例子:

function createObj(o) {
    function F(){}
    F.prototype = o;
    return new F();
}
var person = {
    name: 'kevin',
    friends: ['daisy', 'kelly']
}
var person1 = createObj(person);
var person2 = createObj(person);
person1.name = 'person1';
console.log(person2.name); // kevin
person1.firends.push('taylor');
console.log(person2.friends); // ["daisy", "kelly", "taylor"]

\color{red}{注意:} 修改person1.name的值,person2.name的值并未發(fā)生改變监婶,并不是因為person1和person2有獨立的 name 值旅赢,而是因為person1.name = 'person1'齿桃,給person1添加了 name 值,并非修改了原型上的 name 值煮盼。

5.寄生組合式繼承

?引用《JavaScript高級程序設計》中對寄生組合式繼承的夸贊就是:

??這種方式的高效率體現(xiàn)它只調用了一次 Parent 構造函數(shù)短纵,并且因此避免了在 Child.prototype 上面創(chuàng)建不必要的、多余的屬性僵控。與此同時香到,原型鏈還能保持不變;因此报破,還能夠正常使用 instanceof 和 isPrototypeOf悠就。開發(fā)人員普遍認為寄生組合式繼承是引用類型最理想的繼承范式。

function Parent (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
    console.log(this.name)
}
function Child (name, age) {
    Parent.call(this, name);
    this.age = age;
}

// 關鍵的三步
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
//上面三步等價于:Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child; 
var child1 = new Child('kevin', '18');
console.log(child1);
console.log(child1 instanceof Child) //true充易;
console.log(child1 instanceof Parent) //true;
console.log(Child.prototype.isPrototypeOf(child1)); // true
console.log(Parent.prototype.isPrototypeOf(child1)); // true
console.log(child1.constructor.name); //Child,注意不加Child.prototype.constructor = Child;結果是Parent 

6.ES6-Class繼承

主要是了解幾個關鍵字:
??① class關鍵字梗脾。②extends關鍵字。③super關鍵字

例子:

class Person{
       constructor(name, age) { 
          this.name = name;
          this.age = age;
       } 
       sayName(){
          console.log("the name is:"+this.name); 
       }
 }  
 class Worker extends Person{ 
    constructor(name, age,job) { 
         super(name, age); 
         this.job = job;
    }    
    sayJob(){
      console.log("the job is:"+this.job) 
   }
}   
var worker = new Worker('tcy',20,'teacher'); 

console.log(worker.age); //20
worker.sayJob();//the job is:teacher  
worker.sayName();//the name is:tcy

??上面說到子類的構造函數(shù)constructor中super方法實現(xiàn)對父類構造函數(shù)的調用盹靴。在調用時需要注意兩點:
????1炸茧、子類構造函數(shù)中必須調用super方法,否則在新建對象時報錯稿静。
????2梭冠、子類構造函數(shù)中必須在使用this前調用super,否則報錯。

class Person  {
  constructor(name, age) {
     this.name = name;
     this.age = age;
   }
   sayName(){
       console.log("the name is:"+this.name);
   }
}
//情況1:
class Worker1 extends Person{
   constructor(name, age,job) {
       //報錯
   }
   sayJob(){
     console.log("the job is:"+this.job)
   }
}
//情況2:
class Worker2 extends Person{
   constructor(name, age,job) {
          this.name = name;
         super(name, age);//報錯
         this.job = job;
   }
   sayJob(){
     console.log("the job is:"+this.job)
   }
}

??· super函數(shù)除了表示父類的構造函數(shù)改备,也可以作為父類的對象使用控漠,用于子類調用父類的方法。

class Person  {
  constructor(name, age) {
     this.name = name;
     this.age = age;
   }
   sayName(){
       console.log("the name is:"+this.name);
   }
}
class Worker extends Person{
   constructor(name, age,job) {
         super(name, age);
         this.job = job;
   }
   sayJob(){
     console.log("the job is:"+this.job)
   }
   sayName(){
     super.sayName();//調用父類的方法绍妨,
     console.log("the worker name is:"+this.name)
   }
}
var worker = new Worker('tcy',20,'teacher');
worker.sayJob();//the job is:teacher  
worker.sayName();//the name is:tcy    the worker name is:tcy

· super調用屬性:(有坑)
坑:

class Person  {
  constructor(name, age) {
     this.name = name;
     this.age = age;
   }
   sayName(){
       console.log("the name is:"+this.name);
   }
}
class Worker extends Person{
   constructor(name, age,job) {
         super(name, age);
         this.job = job;
   }
   sayJob(){
     console.log("the job is:"+this.job)
   }
   sayName(){
     console.log(super.name);//調用父類的屬性润脸,undefined
     //super.name報了undefined,表示沒有定義他去。super是指向父類的prototype對象毙驯,即Person.prototype,父類的方法是定義在父類的原型中灾测,而屬性是定義在父類對象上的爆价,所以需要把屬性定義在原型上。
     console.log("the worker name is:"+this.name)
   }
}

需要把屬性定義在原型上:

class Person  {
  constructor(name, age) {
     Person.prototype.name = name;//定義到原型上
     this.age = age;
   }
   sayName(){
       console.log("the name is:"+Person.prototype.name); //注意這里不是this.name
   }
}
class Worker extends Person{
   constructor(name, age,job) {
         super(name, age);
         this.job = job;
   }
   sayJob(){
     console.log("the job is:"+this.job)
   }
   sayName(){
     console.log(super.name);//調用父類的原型屬性媳搪,tcy
     console.log("the worker name is:"+this.name)
   }
}
 var worker = new Worker('tcy',20,'teacher');
 worker.sayJob();//the job is:teacher
 worker.sayName();//tcy,the worker name is:tcy

· this指向的問題:

class Person  {
        constructor(name, age) {
             this.name = name;
             this.age = age;
             this.job = 'doctor';//父類定義的job
        }
       sayName(){
             console.log("the name is:"+this.name);
        } 
       sayJob(){
             console.log("the person job is:"+this.job);
       }
} 
class Worker extends Person{
    constructor(name, age,job) {
          super(name, age); 
         this.job = job;
    }
    sayJob(){
      super.sayJob();//the person job is:teacher
    } 
    sayName(){
      console.log(super.name);//調用父類的原型屬性铭段,tcy
    }
}
   var worker = new Worker('tcy',20,'teacher'); 
   worker.sayJob();//the job is:teacher

父類定義了this.job的值為"doctor",子類定義了this.job值為'teacher'秦爆,調調用父類的方法sayJob(),結果輸出的是子類的值序愚。子類在調用父類構造函數(shù)時,父類的原型this值已經指向了子類等限,即Person.prototype.call(this)爸吮,故輸出的子類的值芬膝。




最優(yōu)解:

??繼承的最優(yōu)解其實是要看當前應用場景的,最符合預期的場景就是形娇,需要共享的锰霜,無論是靜態(tài)的還是動態(tài)的,把它們放在parent的原型上桐早,需要隔離的癣缅,把它們放在parent上,然后子類通過調用parent的構造方法來初始化為自身的屬性哄酝,這樣友存,才是真正的“最佳繼承設計方式”。


參考鏈接

如何回答關于 JS 的繼承
JavaScript深入之繼承的多種方式和優(yōu)缺點
ES6系列教程第六篇--Class繼承

版權聲明:本文為博主原創(chuàng)文章炫七,遵循 CC 4.0 BY-SA 版權協(xié)議爬立,轉載請附上原文出處鏈接和本聲明。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末万哪,一起剝皮案震驚了整個濱河市侠驯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌奕巍,老刑警劉巖吟策,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異的止,居然都是意外死亡檩坚,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門诅福,熙熙樓的掌柜王于貴愁眉苦臉地迎上來匾委,“玉大人,你說我怎么就攤上這事氓润÷咐郑” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵咖气,是天一觀的道長挨措。 經常有香客問我,道長崩溪,這世上最難降的妖魔是什么浅役? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮伶唯,結果婚禮上觉既,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好瞪讼,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布岭参。 她就那樣靜靜地躺著,像睡著了一般尝艘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上姿染,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天背亥,我揣著相機與錄音,去河邊找鬼悬赏。 笑死狡汉,一個胖子當著我的面吹牛,可吹牛的內容都是我干的闽颇。 我是一名探鬼主播盾戴,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼兵多!你這毒婦竟也來了尖啡?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤剩膘,失蹤者是張志新(化名)和其女友劉穎衅斩,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怠褐,經...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡畏梆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了奈懒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奠涌。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖磷杏,靈堂內的尸體忽然破棺而出溜畅,到底是詐尸還是另有隱情,我是刑警寧澤茴丰,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布达皿,位于F島的核電站,受9級特大地震影響贿肩,放射性物質發(fā)生泄漏峦椰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一汰规、第九天 我趴在偏房一處隱蔽的房頂上張望汤功。 院中可真熱鬧,春花似錦溜哮、人聲如沸滔金。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽餐茵。三九已至科阎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間忿族,已是汗流浹背锣笨。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留道批,地道東北人错英。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像隆豹,于是被迫代替她去往敵國和親椭岩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內容