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