詳細解讀js中的繼承機制
前言
最近在學vue,到週末終於有空寫一些東西了(想想又能騙贊,就有點小激動!)。在javascript基礎中,除了閉包之外,繼承也是一個困難。因為考慮到篇幅較長,所以打算分成兩個部分來寫。同樣基於《javascript高級程式設計》,做一個詳細的講解,如果有不對的地方歡迎指正。
準備知識
為了更好的講解繼承,先把一些準備知識放在前面。
1.建構函數,實例
建構函數,是用來建立物件的函數,本質上也是函數。與其他函數的差別在於呼叫方式不同:
如果透過
new
運算子來呼叫的,就是建構子#如果沒有透過
new
運算子來呼叫的,就是普通函數
範例:
function Person(name, age) { this.name = name; this.age = age; } //当做构造函数调用 var person1 = new Person('Mike',10); //当做普通函数调用,这里相当于给window对象添加了name和age属性,这个不是重点,只要注意调用方式 Person('Bob',12); console.log(person1)//Person {name: "Mike", age: 10} console.log(name)//Bob console.log(age)//12
在var person1 = new Person('Mike',10);
中,透過new運算子呼叫了函數Person
,並且產生了person1
,
這裡的Person就稱為建構函數,person1
稱為Person
函數物件的一個實例。實例中會有一個constructor
屬性,指向對應的建構子,看下面的例子:
function Person(name, age) { this.name = name; this.age = age; } var person1 = new Person('Mike',10); var person2 = new Person('Alice',20); console.log(person1.constructor)//function Person(){省略内容...} console.log(person2.constructor)//function Person(){省略内容...}
2.原型物件
當我們每次建立一個函數的時候,函數物件都會有一個prototype
屬性,這個屬性是一個指標,指向它的原型物件。 原型物件的本質也是一個物件。初次看這句話可能有點難以理解,舉個例子,還是剛剛那個函數:
function Person(name, age) { this.name = name; this.age = age; } console.log(Person.prototype)//object{constructor:Person}
可以看到Person.prototype
指向了一個對象,即Person的原型物件,而這個物件有一個constructor
屬性,又指向了Person
函數物件。是不是有點暈?沒關係,接下來我們就上比舉例子更好的手段--畫圖。
3.建構函數,原型物件和實例的關係
在前面,我們剛剛介紹過了建構函數,實例和原型對象,接下來我們用一張圖來表示這三者之間的關係(用ps畫這種圖真是麻煩的要死,大家有好的工具推薦一下):
#從圖上我們可以看到:
函數物件的
prototype
指向原型對象,原型物件的constructor
指向函數物件實例物件的
[Protoptype]
屬性指向原型物件,這裡的[Protoptype]
是內部屬性,可以先理解為它是存在的,但不允許我們存取(雖然在有些瀏覽器是允許存取這個屬性的,但是我們先這樣理解),這個屬性的作用是:允許實例透過該屬性存取原型物件中的屬性和方法。比如說:
function Person(name, age) { this.name = name; this.age = age; } //在原型对象中添加属性或者方法 Person.prototype.sex = '男'; var person1 = new Person('Mike',10); var person2 = new Person('Alice',20); //只给person2设置性别 person2.sex = '女'; console.log(person1.sex)//'男' console.log(person2.sex)//'女'
這裡我們沒有給person1
實例設定sex
屬性,但是因為[Protoptype]
的存在,會存取原型物件中對應的屬性;
同時我們給person2設定sex
屬性後輸出的是'女',說明只有當實例本身不存在對應的屬性或方法時,才會去找原型物件上的對應屬性或方法
繼承
原型鏈
在js中,繼承的主要想法就是利用原型鏈,因此如果理解了原型鏈,繼承問題就理解了一半。這裡可以稍微休息一下,如果對前面的準備知識已經理解差不多了,就開始講原型鏈了。
原型鏈的原理是:讓一個引用型別繼承另一個引用型別的屬性和方法。
先回顧剛剛講過的知識:
原型物件透過
constructor
屬性指向建構函數實例透過
[Prototype]
屬性指向原型物件
如果讓原型物件等於另一個建構子的實例會怎麼樣? 例如:
function A() { } //在A的原型上绑定sayA()方法 A.prototype.sayA = function(){ console.log("from A") } function B(){ } //让B的原型对象指向A的一个实例 B.prototype = new A(); //在B的原型上绑定sayB()方法 B.prototype.sayB = function(){ console.log("from B") } //生成一个B的实例 var a1 = new A(); var b1 = new B(); //b1可以调用sayB和sayA b1.sayB();//'from B' b1.sayA();//'from A'
##現在結合圖片來看程式碼:
- 首先,我們創建了A和B兩個函數對象,
- 同時也產生了它們的原型對象
接著,我們為A的原型物件加入了
sayA()
方法
* 然後是關鍵性的一步B.prototype = new A() ;
,我們讓函數物件B的protytype
指標指向了一個A的實例,請注意我的描述:是讓函數物件B的protytype
指標指向了一個A的實例,這也是為什麼最後,B的原型物件裡面不再有constructor屬性,其實B本來有一個真正的原型對象,原本可以通過B.prototype訪問,但是我們現在改寫了這個指針,使它指向了另一個對象,所以B真正的原型對象現在沒法被訪問了,取而代之的這個新的原型對像是A的一個實例,自然就沒有constructor
屬性了接下來我們給這個B.prototype指向的對象,增加一個
sayB
方法然後,我們產生了一個實例b1
#最後我們呼叫了b1的sayB方法,可以執行,為什麼?
因為b1有[Prototype]
屬性可以存取B prototype裡面的方法;- ##我們呼叫了b1的sayA方法,可以執行,為什麼?
因為b1沿著[Prototype]屬性可以存取B prototype,B prototype繼續沿著
[Prototype]屬性存取A prototype,最後在A.prototype上找到了sayA()方法,所以可以執行
b1繼承了A的屬性和方法,這種由[Prototype]不斷把實例和原型物件連結起來的結構就是原型鏈
。也是js中,繼承主要的實作方式。
以上是詳細解讀js中的繼承機制的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

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

熱門話題

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

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

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

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

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

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

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

在以下情況下不應使用C++函數繼承:衍生類別需要不同實作時,應建立具有不同實作的新函數。衍生類別不需要函數時,應宣告為一個空類別或使用私有、未實作的基底類別成員函數來停用函數繼承。函數不需要繼承時,應使用其他機制(例如範本)來實作程式碼重用。
