以前有朋友問怎麼去理解原型和原型鏈的問題。這個問題,在面試中,不少同窗常常都會遇到。這裏給你們講講,方便你們記憶。
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來檢測類型:
由上面講的instanceof的結果,能夠判斷這些類型的繼承層級:
事實上instanceof是經過原型鏈來檢測類型的,例如L instanceof R: 若是R.prototype出如今了L的原型鏈上則返回true,不然返回false。
用JavaScript來描述instanceof的實現邏輯是這樣的:
JavaScript原型鏈
先給你們看一個JavaScript的原型鏈結構圖。
理解原型鏈的小技巧: 將__proto__箭頭視做泛化(子類到父類)關係!
那麼圖中全部的虛線將構成一個繼承層級,而實線表示屬性引用。
圖中給出了Object.prototype.__proto__ == null,但它尚未標準化,在Chrome、Safari和Node.js下它是不一樣的東西。
但能夠看到JavaScript中全部對象的共同隱式原型爲Object.prototype,它的上一級隱式原型是什麼已經不重要了, 由於它不會影響全部內置對象以及用戶定義類型的原型鏈結構。
上圖其實已經解釋了不一樣內置對象instanceof的行爲,咱們來看Function和Object的特殊之處:
Object是由Function建立的:由於Object.__proto__ === Funciton.prototype;
同理,Function.prototype是由Object建立的;
Funciton是由Function本身建立的!
Object.prototype是憑空出來的!
如今咱們能夠解釋特殊對象的instance行爲了:
另外能夠看到當你聲明一個函數(好比Animal)時,Animal.prototype會自動被賦值爲一個繼承自Object的對象, 並且該對象的constructor等於Animal。即:
值得注意的是Animal若是被Cat繼承,Cat實例(好比cat)的constructor仍然是Animal。
總結
1.每一個函數對象都有一個 prototype 屬性,這個屬性就是函數的原型對象。
2.原型鏈是JavaScript實現繼承的重要方式,原型鏈的造成是真正是靠__proto__ 而非prototype。面試
喜歡文章的能夠關注小編~編程