史上最爲詳細的javascript繼承

前言

        爲你們分享js中最多見最詳細的繼承方式,接下來將一下面的幾個維度進行展現說明

文章有點長,請耐心閱讀😁,有什麼錯誤理解的地方但願留言指出來bash

  • 產生緣由
  • 代碼實現
  • 基本原理
  • 語言實現
  • 場景優勢
  • 缺點

繼承方式

  • 原型鏈繼承
  • 借用構造函數模式繼承
  • 組合繼承
  • 原型式繼承
  • 寄生式繼承
  • 寄生組合

原型鏈繼承

        相信小夥伴們都知道到原型鏈繼承(ECMAScript 中描述了原型鏈的概念,並將原型鏈做爲實現繼承的主要方法),由於原型鏈繼承很是的強大,可是也有它的缺點,接下來我們就按照上面的維度看看原型鏈繼承究竟是什麼鬼app

代碼實現:(須要兩個構造函數來完成一個原型鏈繼承)

// SuperType 構造函數稱爲超類
 function SuperType (){
     this.name='super';
     this.friend=[];
     this.property = true; 
}
SuperType.prototype.getName=function(){
    return this.name;
}
SuperType.prototype.getSuperValue = function(){
 return this.property;
}; 
// SubType 構造函數稱爲子類
function SubType(name,age){
   this.name=name;
   this.age=age;
   this.subproperty = false; 
}
SubType.prototype=new SuperType();
SubType.prototype.constrcutor=SubType;
SubType.prototype.getAge=function(){
    return this.age;
}
SubType.prototype.getSubValue = function (){
 return this.subproperty;
}; 
var child = new SubType('shiny',12);
console.log(child.getName)//shiny
console.log(child.getAge())//12
複製代碼

圖解部分 屬性

基本原理

使用相似做用域的原型鏈,進行繼承查找

語言實現

        定義兩個構造函數,分別爲父類(SuperType)、子類(SubType),爲了實現子類可以使用父類的屬性(自己和原型上面的屬性)。重寫子類的原型,讓子類的原型指向父類實例,這樣子類的構造函數就是父類的實例地址,實現子類可使用父類的自己和原型上的屬性

優勢

        子類能夠經過原型鏈的查找,實現父類的屬性公用與子類的實例函數

缺點

        
  • 一些引用數據操做的時候會出問題,兩個實例會公用繼承實例的引用數據類
  • 謹慎定義方法,以避免定義方法也繼承對象原型的方法重名
  • 沒法直接給父級構造函數使用參數

借用構造函數模式繼承

        雖然原型鏈繼承很強大可是也有他的缺點,借用構造函數繼承能夠解決原型鏈繼承的缺點,開線面的解釋

代碼實現:

// 把父類當中一個函數使用
function SuperType(name){
this.name=name
this.friend=['a','b']
}
SuperType.prototype.getFriend=function(){
  return this.firend
}
function SubType(name){
  // 執行父類函數
  SuperType.call(this,name);
}
var child = new SubType('shiny')
var childRed = new SubType('red')
console.log(child.name)//shiny
console.log(childRed.name)//red
child.firend.push('c')
console.log(child.friend)//a,b,c
console.log(childRed.friend)//a,b
console.log(childRed.getFriend)//undefined
複製代碼

基本原理

使用call apply方法,經過執行方法修改tihs (上下文),是的父級的this變成子類實例的this,這樣每一個實例都會獲得父類的屬性,實現引用屬性備份

使用場景

父類中須要一些子類使用共享的引用類型,而且子類可能會操做父類共享的引用類型 可是父類的非this綁定的屬性和方法是不可使用的(放在父類prototype的屬性和方法)

語言實現

不要把父類當中構造函數,當中一個函數來處理這樣更容易理解,在子類的構造函數中借用父類函數經過修改this來執行,這樣子類的實例包含父類的屬性

優勢

  • 解決了原型鏈繼承的 引用類型操做問題
  • 解決了父類傳遞參數問題

缺點

  • 僅僅使用借用構造函數模式繼承,沒法擺脫夠着函數。方法在構造函數中定義複用不可談
  • 對於超類的原型定義的方法對於子類是不可以使用的,子類的實例只是獲得了父類的this綁定的屬性 考慮到這些缺點,單獨使用借用構造函數也是不多使用的

組合繼承

        上面的兩種繼承方式(原型鏈繼承+借用構造函數繼承),都有本身優缺點,可是他們不是很完美,下面解釋一下組合繼承

代碼實現:

代碼實現:
function SuperType(name){
    this.name=name;
    this.firend=['a','b']
}
SuperType.prototype.getName=function(){
   return this.name
}
function SubType(name,age){
   this.age=age;
   SuperType.call(this,name)
}
SubType.prototype=new SuperType();
SubType.prototype.constrcutor = SubType;
SubType.prototype.getAge=function(){
   return this.age
}
var childShiny=new SubType('shiny',23);
var childRed = new SubType('red',22);
childShiny.firend.push('c');
childRed.firend.push('d');
console.log(childShiny.getName());
console.log(childShiny.getAge());
console.log(childRed.getName());
console.log(childRed.getAge());
console.log(childRed.friend);//[a,b,d]
console.log(childShiny.friend);//[a,b,c]
複製代碼

基本原理

  • 使用原型鏈的繼承實現,經過原型查找功能來知足原型鏈共享方法
  • 使用借用構造函數方法,使用實例備份父類共享引用類型備份

使用場景

獲得原型鏈繼承和構造函數繼承的優勢,是被開發人員承認的一種繼承方式,可是也有他的缺點

語言實現

  • 定義兩個構造函數,分別爲父類(SuperType)、子類(SubType),爲了實現子類可以使用父類的屬性(自己和原型上面的屬性)。重寫子類的原型,讓子類的原型指向父類實例,這樣子類的構造函數就是父類的實例地址,實現子類可使用父類的自己和原型上的屬性
  • 不要把父類當中構造函數,當中一個函數來處理這樣更容易理解,在子類的構造函數中借用父類函數經過修改this來執行,這樣子類的實例包含父類的屬性

優勢

  • 解決了原型鏈繼承引用類型的實例操做致使引用改變
  • 解決了借構造函數繼承方式的,父類原型子類實例可使用

缺點

* 父類的構造函數被實例換了兩次 * 實例會有父類的構造函數的一些this屬性、子類的構造函數(prototype)上也有一份實例的上有的屬性

原型式繼承

        話說上面的的組合繼承不是已經被開發者承認了嗎,原型式繼承是啥?下面我們看看原型式繼承是什麼樣的。

代碼實現:

代碼實現:
1  function object(o){
  function F(){};
  F.prototype=o;
   return new F()
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var personShiny = object(person);
var personRed = object(person);
console.log(personShiny.name)//Nicholas
console.log(personRed.name)//Nicholas
personShiny.friends.push('red');
personRed.friends.push('shiny');
console.log(personShiny.friends)//["Shelby", "Court", "Van","red","shiny"]
//ECMAScript 5 經過新增 Object.create()方法規範化了原型式繼承。這個方法接收兩個參數:一
//個用做新對象原型的對象和(可選的)一個爲新對象定義額外屬性的對象。在傳入一個參數的狀況下,
//Object.create()與 object()方法的行爲相同。
2 
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var personShiny = Object.create(person);
var personRed = Object.create(person);
console.log(personShiny.name)//Nicholas
console.log(personRed.name)//Nicholas
personShiny.friends.push('red');
personRed.friends.push('shiny');
console.log(personShiny.friends)//["Shelby", "Court", "Van","red","shiny"]
複製代碼

基本原理

經過Object.create()方法來建立一個有基礎類的實例,這實例的__proto__指向基礎類ui

使用場景

在不使用構造函數的狀況下,只想讓一個對象與另外一個對象保持相似的狀況下

語言實現

須要建立一個基礎對象,做爲一個新對象的基礎對象,經過object方法或者Object.create方法處理獲得一個新實例,這個新實例上的__proto__指向基礎對象

優勢

再不用建立構造函數的狀況下,實現了原型鏈繼承,代碼量減小一部分this

缺點

  • 一些引用數據操做的時候會出問題,兩個實例會公用繼承實例的引用數據類
  • 謹慎定義方法,以避免定義方法也繼承對象原型的方法重名
  • 沒法直接給父級構造函數使用參數

寄生繼承

        我們看了上面的原型式繼承,其實就是和原型鏈繼承差異不大,只是省去了構造函數這一部,可是原型式繼承也是有缺點的(不可以給備份的對象添加屬性),下面寄生繼承來解決。

代碼實現:

代碼實現:
// 和工廠模式很是相似,建立一個對象,加強一些功能並返回該對象
function createAnother(o){
   var clone = Object(o);
   clone.sayHi=function(){
     console.log('hi')
}
   return clone
}
var person = {
   name:'shiny',
   friends:['a','b']
}
var personShiny = createAnother(person);
console.log(personShiny.sayHi())//Ho
複製代碼

基本原理

備份一個對象,而後給備份的對象進行屬性添加,並返回spa

使用場景

在考不使用構造函數的狀況下實現繼承,前面示 範繼承模式時使用的 object()函數不是必需的;任何可以返回新對象的函數都適用於此模式

語言實現

相似構造函數,經過一個執行方法,裏面建立一個對象,爲該對象添加屬性和方法,而後返回

優勢

  • 再不用建立構造函數的狀況下,實現了原型鏈繼承,代碼量減小一部分
  • 能夠給備份的對象添加一些屬性

缺點

相似構造函數同樣,建立寄生的方法須要在clone對象上面添加一些想要的屬性,這些屬性是放在clone上面的一些私有的屬性prototype

寄生組合繼承

        我們看了上面的組合繼承看上去已經很完美了,可是也有缺點(父類被實例化兩次、子類實例和子類的構造函數都有相同的屬性),寄生組合就是來解決這些問題的

代碼實現:

代碼實現:
 function inheritPrototype({SubType,SuperType}){
      const prototype = Object(SuperType.prototype);
      prototype.constrcutor=SubType;
      SubType.prototype=prototype;
}
function SuperType(name){
    this.name=name;
    this.friends=['a','b']
}
SuperType.prototype.getName=function(){
   return this.name;
}
function SubType(name,age){
    this.age=age;
    SuperType.call(this,name)
}
inheritPrototype({SubType,SuperType});
SubType.prototype.getAge=function(){
   return this.age
}
var SubTypeShiny = new SubType('Shiny',23);
SubTypeShiny .friends.push('c')
var SubTypeRed = new SubType('Red',21);
SubTypeRed .friends.push('d')
console.log(SubTypeShiny.getName())//Shiny
console.log(SubTypeShiny.getAge())//22
console.log(SubTypeShiny.friends)//['a','b','c']
console.log( SubTypeRed.getName())//Red
console.log( SubTypeRed.getAge())//21
console.log( SubTypeRed.friends)//['a','b','d']
複製代碼

基本原理

  • 子類構造函數內經過call、apply方法進行修改父類構造函數的this和執行父類構造函數,使的子類的實例擁有父類構造函數的一些屬性,
  • 結合子類的原型修改爲父類構造函數的原型,並把父類的原型的constructor指向子類構造函數

使用場景

在考不使用構造函數的狀況下實現繼承,前面示 範繼承模式時使用的 object()函數不是必需的;任何可以返回新對象的函數都適用於此模式

語言實現

極度相似組合寄生方式,只是修改了子類原型鏈繼承的方式,組合寄生是繼承父類的實例,寄生組合寄生則是經過一子類的原型繼承父類的原型,並把該原型的constructor指向子類構造函數

優勢

  • 在少一次實例化父類的狀況下,實現了原型鏈繼承和借用構造函數
  • 減小了原型鏈查找的次數(子類直接繼承超類的prototype,而不是父類的實例)

缺點

暫無code

下面是組合繼承和寄生組合繼承的原型圖對比cdn

相關文章
相關標籤/搜索