有沒有想過,爲何咱們可使用內置的方法,例如.length,.split(),.join()咱們的字符串,數組或對象?咱們從未明確指定它們,它們來自何處?如今,不要說「這是JavaScript,沒人知道,這是魔術🧚🏻♂️」,這其實是因爲所謂的原型繼承
。它很是棒,並且您使用它的次數比您想象的要多!javascript
咱們常常必須建立許多相同類型的對象。假設咱們有一個網站,人們能夠瀏覽狗!java
對於每隻狗,咱們都須要表明那隻狗的對象!🐕我將使用構造函數(我知道您在想什麼,稍後將介紹ES6類!),而不是每次都編寫一個新對象。咱們可使用new建立Dog實例(本文並非真的要解釋構造函數,所以,我不會對此進行過多討論)。git
每隻狗都有名字,品種,顏色和吠叫功能!github
建立Dog構造函數時,它不是咱們建立的惟一對象。咱們還自動建立了另外一個對象,稱爲原型(prototype
)!默認狀況下,此對象包含一個構造函數(constructor
),該屬性只是對原始構造函數的引用,在本例中是Dog。數組
Dog的prototype屬性是不可枚舉的,這意味着當咱們嘗試訪問對象的屬性時,該屬性不會顯示。可是它仍然在那裏!函數
好吧..爲何會有這個屬性對象?首先,讓咱們建立一些咱們想要展現的狗。爲簡單起見,我將其稱爲dog1和dog2。dog1是黛西,一個可愛的黑色拉布拉多犬!dog2是傑克,無所畏懼的白人傑克羅素😎oop
咱們到控制檯打印dog1,並展開其屬性!post
咱們看到咱們添加,如性能name,breed,color,和bark..但__proto__是什麼!它是不可枚舉的,這意味着當咱們嘗試獲取對象的屬性時,一般不會顯示它。讓咱們展開它!😃性能
哇,看起來就像Dog.prototype!好吧,__proto__是對Dog.prototype對象的引用。這就是原型繼承
的所有內容:構造函數的每一個實例均可以訪問構造函數的原型!🤯網站
那麼爲何這很酷?有時咱們擁有全部實例共享的屬性。例如本例中的bark:每一個實例都徹底相同。爲何每次建立新狗時都建立一個新函數?這樣很消耗內存!!!相反,咱們能夠將其添加到Dog.prototype對象中!🥳
每當咱們嘗試訪問實例上的屬性時,引擎都會首先在本地搜索以查看該屬性是否在對象自己上定義。可是,若是找不到咱們要訪問的屬性,引擎將沿着該__proto__屬性沿着原型鏈走下去!
如今這只是一個步驟,可是能夠包含多個步驟!您可能已經注意到,當我展開__proto__顯示的對象時,我沒有包括一個屬性Dog.prototype。Dog.prototype自己是一個對象,這意味着它其實是Object構造函數的實例!這意味着Dog.prototype還包含一個__proto__屬性,該屬性是對Object.prototype!的引用。
最後,咱們對全部內置方法的來源都有一個答案:它們在原型鏈上!😃
例如.toString()方法。它不是在dog1上定義的,也不是在dog1.proto(即Dog.prototype)上定義的,是在Dog.prototype.proto(即Object.prototype)上定義的! 🙌🏼
如今,咱們一直在使用構造函數(function Dog() { ... }),它仍然是有效的JavaScript。可是,ES6實際上爲構造函數和原型使用了一種更簡單的語法:類!
類僅是構造函數的語法糖。一切仍然以相同的方式工做!
咱們用class關鍵字編寫類。一個類有一個constructor函數,與咱們用ES5語法編寫的構造函數同樣!咱們要添加到原型的屬性是在類主體自己上定義的。
關於類的另外一個好處是,咱們能夠輕鬆擴展其餘類。
假設咱們要展現幾隻相同品種的狗,即吉娃娃犬!吉娃娃犬仍是狗。爲了保持這個例子簡單,我只經過傳遞name來初始化,而不是如今的name,breed和color。可是這些吉娃娃也能夠作些特別的事情,它們的叫聲很小。除了叫Woof!
,吉娃娃也能夠叫Small woof!
🐕
在擴展類中,咱們可使用super關鍵字訪問父類的構造函數。父類的構造函數指望的參數,在這種狀況下,咱們必須傳遞給super:name。
myPet能夠訪問Chihuahua.prototype和Dog.prototype(而且自動訪問Object.prototype,由於Dog.prototype是一個對象)。
因爲Chihuahua.prototype有smallBark,Dog.prototype有bark,咱們就能夠同時訪問smallBark和bark上myPet!
如今,您能夠想象,原型鏈不會永遠持續下去。最終有一個原型等於null的Object.prototype對象:在這種狀況下就是對象!若是咱們嘗試訪問在本地或原型鏈上找不到的屬性,undefined則會返回。
儘管我在這裏用構造函數和類解釋了全部內容,可是將原型添加到對象的另外一種方法是使用Object.create方法。使用此方法,咱們能夠建立一個新對象,並能夠準確指定該對象的原型!💪🏼
咱們經過將現有對象做爲參數傳遞給Object.create方法來實現。該對象是咱們建立的對象的原型!
讓咱們打印me
剛剛建立的對象。
咱們沒有向該me
對象添加任何屬性,它僅包含__proto__屬性!__proto__屬性包含對咱們定義爲原型的person對象的引用:該對象具備name和age屬性。因爲person對象是對象,所以對象上的__proto__屬性的值person是Object.prototype(但爲了使其更容易閱讀,我沒有在gif中擴展該屬性!)
但願您如今瞭解爲何原型繼承在JavaScript的奇妙世界中如此重要!若有疑問,請隨時與我聯繫!😊