在我初學 JS 語言的繼承機制原型和原型鏈的時候,我一直理解不了這種設計機制,再加上以前原有對 Java繼承的理解,在學習 JS 繼承機制的設計上踩了一個大坑,不少知識點前期都是死記硬背,沒法真正的理解它的設計思想。前端
JS 中的繼承機制思想能夠說是學習 JS 的一個核心思想,更能夠說是 JS 中的一個命脈,每每這些複雜、抽象的繼承關係,以及專業術語、代名詞確成爲了困擾初學者的絆腳石。當我真正理解它的設計思想時,其實並無那麼複雜,並且以爲很是簡單。面試
在寫這篇 JS 的原型和原型鏈的文章以前,我在谷歌搜索檢索了大量的高贊有關 JS 原型和原型鏈的文章,大部分都是圍繞着「是什麼」來說的,致使部分初學者缺乏對 JS 繼承的設計與實現的先後關聯性,仍是很難準確的去理解。算法
咱們先要明白,學習這塊內容知識要知道設計者「 爲何這樣作 」 遠比 「怎麼作的」 重要的多這纔是掌握這部份內容的關鍵。編程
今天小鹿對 JS 的繼承機制要作一個系統的總結,從設計者的角度出發,將複雜的設計思想用動畫呈現,將零碎的知識點體系化,爭取讓你一文搞懂 JS 的繼承機制思想(原型和原型鏈)。瀏覽器
要想貫徹 JS 的核心設計思想,咱們要從 JS 的誕生提及。服務器
相對比較成熟的瀏覽器是由網景公司發佈的,早些年間,瀏覽器只能瀏覽網頁內容,而不能進行用戶交互。好比咱們登陸輸入用戶名和密碼,在瀏覽器是不能進行判斷用戶是否真正輸入了,而是經過服務器來判斷,若是沒有輸入,返回錯誤提示用戶,這種設計很是的浪費時間和服務器資源。 網絡
當時最流行的語言是面向對象的Java編程語言 ,網景公司爲了可以藉助 Java將瀏覽器腳本語言流傳開,因此起名 JavaScript。其實二者沒有任何的關係。數據結構
JS 中的數據類型設計受當時 Java流行的影響,都是對象類型,這時候就遇到問題了,有對象必然涉及到繼承機制,那麼 JS 的繼承機制要設計成 Java同樣呢?仍是另有設計思想?編程語言
JS 的開發者想若是設計成像 Java同樣有「類」的概念豈不是和 Java同樣成爲了一種徹底面向對象的編程語言了?最後決定本身設計一種繼承機制,可是它的設計思想仍是採用了 Java的一些特性。函數
一般 Java 生成對象是經過 new 的方式,經過類生成一個實例對象的過程。可是 JS 中並無類,那 JS 的設計者要怎麼作?
new
的過程內部其實調用了構造函數。可是 JS 是沒有「類」的概念的,因而 JS 就把
new
一個「類」設計成了
new
一個構造函數,因而構造函數成爲了一個實例對象的原型對象。
上述這樣的原型設計有一個致命的缺點就是沒法共享公共屬性。
由於咱們知道,每 new
一個對象,生成的實例是兩個不一樣的對象。因此共有的屬性也不是共享的,若是咱們改動一個對象的 type 屬性,可是另外一個不會改變,由於這個屬性沒有共享。
要想讓構造函數生成的全部實例對象都可以共享屬性,那麼咱們就給構造函數加一個屬性叫作prototype
,用來指向原型對象,咱們把全部實例對象共享的屬性和方法都放在這個構造函數的prototype
屬性指向的原型對象中,不須要共享的屬性和方法放在構造函數中。
這裏有一點疑惑就是,咱們知道對象能夠設置屬性,函數也能夠設置屬性嗎?對於初學者來講是比較懵逼的,那咱們能夠稍微的簡單說一下:
JavaScript 中的函數擁有對象的全部能力,也所以可被稱做爲任意其餘類型對象來對待。當咱們說函數是第一類對象的時候,就是說函數也可以對象的一些功能,好比添加屬性,函數當作參數傳遞等。
因此說,實例對象一旦經過構造函數建立,就會自動給實例對象賦值上原型對象上共享的屬性或方法。說清楚一點就是該對象屬性都指向了原型對象的屬性值。
上述的圖反映了對象以及函數在原型鏈中的關係,若是你覺的上邊的這張圖看懵逼了,不要緊,我剛開始學習原型鏈的時候,根本不知道上邊這是什麼「清明上河圖」,小鹿下面經過一步步的拆分講解,看這張圖就很是簡單,沒錯,很是簡單。
咱們文章的開頭也說了什麼是原型對象,說白了就是構造函數的一個 prototype
屬性,這個屬性就指向原型對象。
其實咱們其中一些鏈接屬性沒有講到,只講到了prototype
屬性,下面一張圖來將剩下的屬性補充完整,咱們只要把這張圖印到大腦中就能夠了。
咱們來分析一下上圖,首先咱們先要聲明一個狗的構造函數,定義其名字和體重屬性(私有屬性),同時每一個構造函數咱們上邊講到了,都會有一個prototype
屬性。
這個prototype
指向的就是原型對象,原型對象放的就是對象共享的屬性。可是注意,原型對象裏有一個constructor
屬性,這個屬性又指回了構造函數。
在 JS 全部對象中,只要是對象,都會有一個內置屬性叫作_proto_
,並且這個屬性是系統自動生成的,只要你建立一個對象,這個對象就有這個屬性。這個_proto_
屬性指向的是原型對象。
經過上邊的分佈講解,咱們明白了構造函數與對象實例以及原型對象的關係。
總結爲一句話爲:
構造函數的 prototype 指向原型對象,原型對象有一個 constructor 屬性指回構造函數,每一個構造函數生成的實例對象都有一個 proto 屬性,這個屬性指向原型對象。
沒錯,原型就是這麼簡單。可是你會發現,原型也是對象呀,你說只要是對象都會有一個_proto_屬性指向自身構造函數的原型對象。
沒錯,要想知道原型對象的_proto_
屬性指向誰,就要知道是哪一個構造函數建立了原型對象?
咱們知道,全部的 JS 對象的都是繼承了一個叫作 Object
的對象。能夠理解爲 Object
構造函數創造了這個萬物,他們的關係以下,和上邊是一樣的道理,上邊總結的那句話好好理解一下。
可是上圖中會有一個疑問,Object
構造函數原型對象的也是對象,它確定也有一個_proto_
屬性,爲何會指向null
呢?
咱們在拿上述總結的那句話,_proto_
屬性指向的是自身構造函數的原型對象,自身的構造函數是誰?是 Object
構造函數,那 Object
構造函數的原型是誰?固然是自己(如圖),因此把_proto_
指向了null
。
上邊的關係若是不仔細整理的話確實很亂,尤爲是對於初學者,可是若是像小鹿這樣已整理,再亂的關係把它安排的層次分明,沒有理解,就多看幾篇文章。
咱們還有一個問題沒有解決就是原型鏈?既然我麼你知道什麼是原型了,原型鏈是什麼?顧名思義,確定是一條鏈,既然每一個對象都有一個_proto_
屬性指向原型對象,那麼原型對象也有_proto_指向原型對象的原型對象,直到指向上圖中的null
,這纔到達原型鏈的頂端。
不要忘了,上邊那種圖咱們尚未把它理解,咱們把圖自上而下理解。
Object
的關係圖嗎?對的,沒錯。
function
換成了
Function
,f 變成了大寫的 F,這裏涉及到一個知識點就是,在 JS 中,全部的
function
函數都是由
Function
繼承來的,能夠說是
Function
是全部
function
的祖宗。
那Function
是由誰生產來的?咱們看到圖中的Function
函數有_proto_
屬性了,並且屬性指向本身的原型對象,那不就是本身繁衍本身嗎?能夠這麼理解。
這裏咱們在縱觀全圖,總結幾條定義你比對着圖去找。
一、全部的實例的_proto_
都指向該構造函數的原型對象(prototype
)。
二、全部的函數(包括構造函數)是Function
的實例,因此全部函數的_proto_
的都指向Function
的原型對象。
三、全部的原型對象(包括 Function
的原型對象)都是Object的實例,因此_proto_
都指向 Object
(構造函數)的原型對象。而Object
構造函數的 _proto_
指向 null。
四、Function
構造函數自己就是Function
的實例,因此_proto_
指向Function
的原型對象。
全篇文章的精華都在最後的總結部分,前邊的全部分解講解是爲了讓你理解這些函數對象以及原型對象之間的關係,這關係都是固定的,誰指向誰,都是寫死額,只要你記住了他們的關係,這張圖就理解的差很少了,可以理解完這張圖,你的原型和原型鏈已經瞭解的很紮實了,可是還須要作一些面試題鞏固一下。
後期會出文章專門講解怎麼解決大廠的這種有關原型和原型鏈的面試題。
今天的文章就到這裏了,今天的內容看起來多,其實總結起來就幾句話,仍是那句話,原創不易,點個贊就是對原創做者最大的支持,很是感謝。
文章都看完了,爲什麼不妨點個贊呢?嘻嘻,那就說明你很自私,你怕那麼好的文章讓別人也看到。開個小小玩笑。
其實我也很自私,我把個人一直以來堅持原創的公衆號:「小鹿動畫學編程」偷偷給你,裏邊匯聚了小鹿以動畫形式講解的數據結構與算法、網絡原理、Web 等技術文章。
做者Info:
【做者】:小鹿
【原創公衆號】小鹿動畫學編程
【簡介】:和小鹿同窗一塊兒用動畫的方式從零基礎學編程,將Web前端領域、數據結構與算法、網絡原理等通俗易懂的呈獻給小夥伴。公衆號回覆 「資料」 送一從零自學資料大禮包!
【轉載說明】:轉載請說明出處,謝謝合做!~