如何理解JavaScript的原型和原型鏈?

WEB開發阿靖發表於2018-12-07

如何理解JavaScript的原型和原型鏈?

之前有朋友在公眾號給我留言,問問怎麼去理解原型和原型鏈的問題。這個問題,在面試中,很多同學經常都會遇到。

回覆多了,覺得大家對這塊知識點理解還是不夠深。於是決定今天來給大家講講,方便大家記憶。

JavaScript的特點

JavaScript是一門直譯式指令碼語言,是一種動態型別、基於原型的語言。 JavaScript的靈活性不亞於C++,你可以使用JavaScript嘗試不同的程式設計範型。

比如類jQuery風格的函數語言程式設計、基於過程的指令式程式設計、以及基於原型的物件導向程式設計。

不同於Java、C#等面嚮物件語言,JavaScript採用基於原型的繼承方式。

為啥會有原型和原型鏈?

1994年,網景公司(Netscape)釋出了Navigator瀏覽器0.9版,但是剛開始的Js沒有繼承機制,更別提像同時期興盛的C++和Java這樣擁有物件導向的概念。在實際的開發過程中,工程師們發現沒有繼承機制很難解決一些問題,必須有一種機制能將所有的物件關聯起來。

Brendan Eich鑑於以上情況,但不想把Js設計得過為複雜,於是引入了new關鍵詞和constructor建構函式來簡化物件的設計,引入了prototype函式物件來包含所有例項物件的建構函式的屬性和方法,引入了proto和原型鏈的概念解決繼承的問題。

原型模式

  • 每個函式都有一個prototype(原型)屬性
  • 這個屬性都有一個指標,指向一個物件
  • 這個物件包含由特定型別所有例項共享的屬性和方法
  • 使用原型的好處是 可以讓所有物件例項共享它包含的方法和屬性

通過in操作符和hasOwnProperty來判斷給定屬性是來自於原型還是例項

in- true 代表屬性在物件中存在 來自例項或者來自原型

hasOwnProperty- true代表屬性來自於例項 是例項屬性

原型鏈

ECMAScript中只支援實現繼承,而且是通過原型鏈的方式來實現的。所以原型鏈是JavaScript實現繼承的一種重要方式。

使用者定義型別的原型鏈

我們一般如何來檢查JavaScript的變數資料型別?一般我們都是通過instanceof關鍵字,可以基於原型鏈來檢測變數的型別。

我們可以先構造一個原型鏈,再用instanceof來檢測型別:

如何理解JavaScript的原型和原型鏈?

如何理解JavaScript的原型和原型鏈?

由上面講的instanceof的結果,可以判斷這些型別的繼承層級:

如何理解JavaScript的原型和原型鏈?

事實上instanceof是通過原型鏈來檢測型別的,例如L instanceof R: 如果R.prototype出現在了L的原型鏈上則返回true,否則返回false。

用JavaScript來描述instanceof的實現邏輯是這樣的:

如何理解JavaScript的原型和原型鏈?

JavaScript原型鏈

先給大家看一個JavaScript的原型鏈結構圖。

如何理解JavaScript的原型和原型鏈?悄悄告訴你理解原型鏈的小技巧: 將__proto__箭頭視作泛化(子類到父類)關係!

那麼圖中所有的虛線將構成一個繼承層級,而實線表示屬性引用。

圖中給出了Object.prototype.__proto__ == null,但它還沒有標準化,在Chrome、Safari和Node.js下它是不同的東西。

但可以看到JavaScript中所有物件的共同隱式原型為Object.prototype,它的上一級隱式原型是什麼已經不重要了, 因為它不會影響所有內建物件以及使用者定義型別的原型鏈結構。

上圖其實已經解釋了不同內建物件instanceof的行為,我們來看Function和Object的特殊之處:

  1. Object是由Function建立的:因為Object.__proto__ === Funciton.prototype;
  2. 同理,Function.prototype是由Object建立的;
  3. Funciton是由Function自己建立的!
  4. Object.prototype是憑空出來的!

現在我們可以解釋特殊物件的instance行為了:

如何理解JavaScript的原型和原型鏈?

另外可以看到當你宣告一個函式(比如Animal)時,Animal.prototype會自動被賦值為一個繼承自Object的物件, 而且該物件的constructor等於Animal。即:

如何理解JavaScript的原型和原型鏈?

值得注意的是Animal如果被Cat繼承,Cat例項(比如cat)的constructor仍然是Animal。

如何理解JavaScript的原型和原型鏈?

總結

1.每個函式物件都有一個 prototype 屬性,這個屬性就是函式的原型物件。

2.原型鏈是JavaScript實現繼承的重要方式,原型鏈的形成是真正是靠__proto__ 而非prototype。

好了,今天的講解就那麼多,如果你還有什麼前端問題想提問的,或者你想李老師下次給大家講什麼內容,可以直接留意提問,說不定下次文章就會講解了。

如果你覺得這篇文章對你有幫助,請轉發點贊支援一下!

相關文章