目錄
1 ES6中的繼承
2 原型鏈繼承
3 借用建構子
4 組合繼承
5 原型式繼承
6 寄生式繼承
7 寄生組合式繼承
首頁 web前端 js教程 js中有哪些繼承方式?

js中有哪些繼承方式?

Jun 21, 2017 am 09:57 AM
javascript 繼承

1 ES6中的繼承

ES6使用class關鍵字定義類,使用extends關鍵字繼承類別。子類別的constructor建構方法中必須呼叫super方法來獲得父類別的」this「對象,呼叫super時可以向父建構子傳參。子類別可以透過super物件直接使用父類別的屬性和方法,也可以透過同名屬性或方法覆寫父類別中的定義。

class Father {
  constructor () {
    this.surname = '王'
    this.money = Infinity
  }
  sayName () {
    console.log(`My surname is ${this.surname}.`)
  }
}

class Son extends Father {
  constructor (firstname) {
    super()
    this.firstname = firstname
  }
  sayName () {
    console.log(`My name is ${super.surname}${this.firstname}.`)
  }
  sayMoney () {
    console.log(`I have ${this.money} money.`)
  }
}

let Sephirex = new Son('撕葱')
Sephirex.sayName()
Sephirex.sayMoney()
登入後複製

ES6中的類別和繼承本質上是使用prototype實作的語法糖,類別中定義的方法相當於在prototype上定義方法,constructor方法中定義屬性相當於建構子模式,super方法相當於在子類別中呼叫父類別的建構子。以下繼續討論ES5中繼承的實作。

2 原型鏈繼承

原型鏈繼承的基本模式,就是讓子類型的原型物件指向父類型的一個實例,然後再為其原型擴展方法。

function Person (name) {
  this.name = name
  this.likes = ['apple', 'orange']
}
Person.prototype.sayName = function () {
  console.log(this.name)
}

function Worker () {
  this.job = 'worker'
}
Worker.prototype = new Person()
Worker.prototype.sayJob = function () {
  console.log(this.job)
}

let Tom = new Worker()
let Jerry = new Worker()
Tom.likes.push('grape')
console.log(Jerry.likes) // [ 'apple', 'orange', 'purple' ]
登入後複製

原理:之前的文章我們討論了__proto__和prototype。子類別的實例中有一個__proto__指針,指向其建構函數的原型物件。而子類別建構子的原型指向父類別的一個實例,父類別實例中的__proto__又指向了父類別建構子的原型......如此層層遞進,就構成了原型鏈。
要注意的是,即使父類別中引用類型的屬性是在建構函式中定義的,還是會被子類別實例共用。這是因為子類別建構子的原型其實是父類別的一個實例,於是父類別的實例屬性自然就變成子類別的原型屬性,而引用類型值的原型屬性會在實例之間共用。
原型鏈的另一個問題是,沒有辦法在不影響所有物件實例的情況下,給父類別的建構子傳遞參數。像上面的例子,用 Worker.prototype = new Person() 將子類別原型指向父類別實例的時候, 如果傳入了初始化參數,則所有子類別的實例name屬性都會是傳入的參數。如果這裡不傳參數,後邊也沒有的辦法為父類別建構子傳參了。因此很少單獨使用原型鏈繼承模式。

3 借用建構子

借用建構子可以解決引用型別屬性被分享的問題。所謂「借用」建構函數,就是在子類別建構子中呼叫父類別的建構子---別忘了函數中 this 的指向跟函數在哪裡定義無關,而只跟在哪裡呼叫有關。我們可以利用call或apply,在子類別實例上呼叫父類別的建構函數,以取得父類別的屬性和方法,類似ES6子類別建構子中呼叫super方法。

function Person (name) {
  this.name = name
  this.likes = ['apple', 'orange']
}

function Worker (name) {
  Person.call(this, name)
  this.job = 'worker'
}

let Tom = new Worker('Tom')
Tom.likes.push("grape")

let Jerry = new Worker('Jerry')

console.log(Tom.likes) // [ 'apple', 'orange', 'grape' ]
console.log(Jerry.likes) // [ 'apple', 'orange' ]
登入後複製

單純使用建構子的問題在於函數無法重複使用,且子類別無法取得父類別prototype上的屬性與方法。

4 組合繼承

組合繼承借用建構子定義實例屬性,使用原型鏈共享方法。組合繼承將原型鏈模式和借用建構函式結合起來,從而發揮二者之長,彌補各自不足,是js中最常用的繼承模式。

function Person (name) {
  this.name = name
  this.likes = ['apple', 'orange']
}
Person.prototype.sayName = function () {
  console.log(this.name)
}

function Worker (name, job) {
  Person.call(this, name) // 第二次调用 Person()
  this.job = job
}
Worker.prototype = new Person() // 第一次调用 Person()
Worker.prototype.constructor = Worker
Worker.prototype.sayJob = function () {
  console.log(this.job)
}

let Tom = new Worker('Tom', 'electrician')
Tom.likes.push('grape')
console.log(Tom.likes) // [ 'apple', 'orange', 'grape' ]
Tom.sayName() // Tom
Tom.sayJob() // electrician

let Jerry = new Worker('Jerry', 'woodworker')
console.log(Jerry.likes) // [ 'apple', 'orange' ]
Jerry.sayName() // Jerry
Jerry.sayJob() // woodworker
登入後複製

組合繼承也並非沒有缺點,那就是繼承過程會兩次呼叫父類別建構子。在第一次呼叫Person 建構函式時,Worker.prototype 會得到兩個屬性:name 和likes;當呼叫Worker 建構函式時,又會呼叫一次Person 建構函數,這次直接建立了實例屬性name 和likes ,覆寫了原型中的兩個同名屬性。

5 原型式繼承

如下的object函數是道格拉斯·克羅克福德在一篇文章中記錄的。在 object函數內部,先創建了一個臨時性的構造函數,然後將傳入的對像作為這個構造函數的原型,最後返回了這個臨時類型的一個新實例。從本質上講, object() 對傳入其中的物件執行了一次淺複製。這種繼承方式,相當於把父類型的屬性和方法複製一份給子類型,然後再為子類型加入各自的屬性和方法。
這種方式同樣會共享引用類型值的屬性。

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

let Superhero = {
  name: 'Avenger',
  skills: [],
  sayName: function () {
    console.log(this.name)
  }
}

let IronMan = object(Superhero)
IronMan.name = 'Tony Stark'
IronMan.skills.push('fly')

let CaptainAmerica = object(Superhero)
CaptainAmerica.name = 'Steve Rogers'
CaptainAmerica.skills.push('shield')

IronMan.sayName() // Tony Stark
console.log(IronMan.skills) // [ 'fly', 'shield' ]
登入後複製

ES5中以 Object.create() 方法規範化了原型式繼承。這個方法接收兩個參數:一個用作新物件原型的物件和(可選的)一個為新物件定義額外屬性的物件。在傳入一個參數的情況下,Object.create() 與 object() 方法的行為相同。 Object.create() 方法的第二個參數與 Object.defineProperties() 方法的第二個參數格式相同。

let CaptainAmerica = Object.create(Superhero, {
  name: {
    value: 'Steve Rogers',
    configurable: false
  }
})
登入後複製

6 寄生式繼承

寄生式繼承很好理解,只是一個封裝了繼承過程的工廠函數。由於方法直接定義在物件上,寄生式繼承新增的方法不能重複使用。

function inherit(parent){
  var clone = Object.create(parent)
  clone.name = 'hulk'
  clone.sayHi = function(){
    console.log("hi")
  }
  return clone
}

let Hulk = inherit(Superhero)

Hulk.sayName() // hulk
Hulk.sayHi() // hi
登入後複製

7 寄生組合式繼承

前面提到組合繼承是js中最常用的繼承方式,但不足是會呼叫兩次父類別的建構子。寄生組合式繼承可以解決這個問題,並且被認為是包含引用類型值的物件最理想的繼承方式。
寄生組合式繼承的基本想法是,不必為了指定子類別的原型而呼叫父類別的建構函數,需要的只是父類別原型的一個副本而已。寄生組合式繼承就是藉用建構函式來繼承屬性,然後使用寄生式繼承來繼承父類別的原型。

function inheritPrototype(subType, superType){
  var prototype = Object.create(superType.prototype)
  prototype.constructor = subType
  subType.prototype = prototype
}

function Person (name) {
  this.name = name
  this.likes = ['apple', 'orange']
}
Person.prototype.sayName = function () {
  console.log(this.name)
}

function Worker (name, job) {
  Person.call(this, name)
  this.job = job
}
inheritPrototype(Worker, Person)

Worker.prototype.sayJob = function () {
  console.log(this.job)
}
登入後複製

以上是js中有哪些繼承方式?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

C++ 函式繼承詳解:如何在繼承中使用「基底類別指標」和「衍生類別指標」? C++ 函式繼承詳解:如何在繼承中使用「基底類別指標」和「衍生類別指標」? May 01, 2024 pm 10:27 PM

在函數繼承中,使用「基底類別指標」和「衍生類別指標」來理解繼承機制:基底類別指標指向派生類別物件時,執行向上轉型,只存取基底類別成員。派生類別指標指向基底類別物件時,執行向下轉型(不安全),必須謹慎使用。

簡易JavaScript教學:取得HTTP狀態碼的方法 簡易JavaScript教學:取得HTTP狀態碼的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

C++ 函式繼承詳解:如何偵錯繼承中出現的錯誤? C++ 函式繼承詳解:如何偵錯繼承中出現的錯誤? May 02, 2024 am 09:54 AM

繼承錯誤調試技巧:確保正確的繼承關係。使用偵錯器逐步執行程式碼,檢查變數值。確保正確使用virtual修飾符。檢查隱藏的繼承帶來的菱形繼承問題。檢查抽象類別中未實現的純虛函數。

C++ 函式繼承詳解:如何理解繼承中的「is-a」與「has-a」關係? C++ 函式繼承詳解:如何理解繼承中的「is-a」與「has-a」關係? May 02, 2024 am 08:18 AM

C++函式繼承詳解:掌握「is-a」和「has-a」關係什麼是函式繼承?函數繼承是C++中一種將衍生類別中定義的方法與基底類別中定義的方法關聯起來的技術。它允許衍生類別存取和重寫基底類別的方法,從而擴展了基底類別的功能。 「is-a」和「has-a」關係在函數繼承中,「is-a」關係指派生類別是基底類別的子類型,也就是說,衍生類別「繼承」了基底類別的特性和行為。 「has-a」關係指派生類別包含對基底類別物件的參考或指針,也就是說,衍生類別「擁有」了基底類別物件。語法以下是如何實作函數繼承的語法:classDerivedClass:pu

C++ 中繼承和多態性如何影響類別的耦合度? C++ 中繼承和多態性如何影響類別的耦合度? Jun 05, 2024 pm 02:33 PM

繼承和多態性會影響類別的耦合度:繼承會增加耦合度,因為衍生類別依賴基底類別。多態性可以降低耦合度,因為物件可以透過虛擬函數和基底類別指標以一致的方式回應訊息。最佳實踐包括謹慎使用繼承、定義公共介面、避免在基底類別中新增資料成員,以及透過依賴注入解耦類別。實戰案例顯示如何使用多態性和依賴注入來降低銀行帳戶應用程式中的耦合度。

如何在JavaScript中取得HTTP狀態碼的簡單方法 如何在JavaScript中取得HTTP狀態碼的簡單方法 Jan 05, 2024 pm 01:37 PM

JavaScript中的HTTP狀態碼取得方法簡介:在進行前端開發中,我們常常需要處理與後端介面的交互,而HTTP狀態碼就是其中非常重要的一部分。了解並取得HTTP狀態碼有助於我們更好地處理介面傳回的資料。本文將介紹使用JavaScript取得HTTP狀態碼的方法,並提供具體程式碼範例。一、什麼是HTTP狀態碼HTTP狀態碼是指當瀏覽器向伺服器發起請求時,服務

'PHP物件導向程式設計入門:從概念到實踐” 'PHP物件導向程式設計入門:從概念到實踐” Feb 25, 2024 pm 09:04 PM

什麼是物件導向程式設計?物件導向程式設計(OOP)是一種程式設計範式,它將現實世界中的實體抽象化為類,並使用物件來表示這些實體。類別定義了物件的屬性和行為,而物件則實例化了類別。 OOP的主要優點在於它可以使程式碼更易於理解、維護和重複使用。 OOP的基本概念OOP的主要概念包括類別、物件、屬性和方法。類別是物件的藍圖,它定義了物件的屬性和行為。物件是類別的實例,它具有類別的所有屬性和行為。屬性是物件的特徵,它可以儲存資料。方法是物件的函數,它可以對物件的資料進行操作。 OOP的優點OOP的主要優點包括:可重複使用性:OOP可以讓程式碼更

Java 介面與抽象類別:通往程式設計天堂之路 Java 介面與抽象類別:通往程式設計天堂之路 Mar 04, 2024 am 09:13 AM

介面:無實作的契約介面在Java中定義了一組方法簽名,但不提供任何具體實作。它充當一種契約,強制實作該介面的類別實現其指定的方法。介面中的方法是抽象方法,沒有方法體。程式碼範例:publicinterfaceAnimal{voideat();voidsleep();}抽象類別:部分實作的藍圖抽象類別是一種父類,它提供了一個部分實現,可以被它的子類別繼承。與介面不同,抽象類別可以包含具體的實作和抽象方法。抽象方法是用abstract關鍵字聲明的,並且必須被子類別覆蓋。程式碼範例:publicabstractcla

See all articles