一篇文章讓你搞懂JavaScript 原型和原型鏈
本文由葡萄城技術團隊原創并首發
轉載請注明出處:葡萄城官網
與多數面向對象的開發語言有所不同,雖然JavaScript沒有引入類似類的概念(ES6已經引入了class語法糖),但它仍然能夠大量的使用對象,那么如何將所有對象聯系起來就成了問題。于是就有了本文中我們要講到的原型和原型鏈的概念。
原型和原型鏈作為深入學習JavaScript最重要的概念之一,如果掌握它了后,弄清楚例如:JavaScript的繼承,new關鍵字的原來、封裝及優化等概念將變得不在話下,那么下面我們開始關于原型和原型鏈的介紹。
什么是原型?
JS中的對象包含了一個prototype的內部屬性,這個屬性所對應的就是該對象的原型。
我們先看下圖:a、b、c 分別為數組、對象、函數。
可以看到,三者都有一個屬性:__proto__
這個 __proto__ 稱作 隱式原型。
除此之外,c還有一個屬性:prototype
這個prototype 稱作 顯式原型。
小結一下:
所有引用類型(函數,數組,對象)都擁有__proto__屬性(隱式原型) 所有函數除了有_proto_屬性之外還擁有prototype屬性(顯式原型) 原型對象:每創建一個函數,該函數會自動帶有一個prototype屬性,該屬性是一個指針,指向了一個對象,我們稱之為原型對象。函數除了有_proto_屬性之外還擁有prototype屬性,我們借助構造函數來尋找二者之間的關系。如下圖:
總結如下(結合上圖更容易理解):
1. 實例對象a只有__proto__(隱式原型),構造函數既有 __proto__(隱式原型)也有prototype(顯式原型)
2. __proto__ 和 prototype 都是一個對象,既然是對象,就表示他們也有一個 __proto__
a.__proto__.__proto__A.prototype.__proto__
3.實例對象a的隱式原型指向它構造函數的顯式原型,指向的意思是恒等于
a.__proto__ === A.prototype
4. 當調用某種方法或查找某種屬性時,首先會在自身調用和查找,如果自身并沒有該屬性或方法,則會去它的__proto__屬性中調用查找,也就是它構造函數的prototype中調用查找。
什么是原型鏈?
先看下圖,提出一個問題:
1. 在 Object的顯式原型添加屬性b,為什么 示例對象p 能使用此屬性呢? p.b = b
2. 為什么 p.a 為undefined
如下圖所示
1. 實例對象p的隱式原型(__proto__)是一個對象,有兩個屬性值:constructor 和 __proto__
2. p.__proto__.constructor 返回的結果為構造函數Person
3. p.__proto__.__proto__ .constructor 返回的結果為Object()函數
結合上面所講的顯式原型與隱式原型之間的關系,等同如下:
p.__proto__.__proto__ = Object.prototype
所以p.b打印結果為b,p沒有b屬性,會一直通過__proto__向上查找,最后當查找到Object.prototype時找到,最后打印出b,向上查找過程中,得到的是Object.prototype,而不是Function.prototype,找不到a屬性,所以結果為undefined,這就是 原型鏈,通過__proto__向上進行查找,最終到null結束。
總結:
1. 查找屬性,如果本身沒有,則會去__proto__中查找,也就是構造函數的顯式原型中查找,如果構造函數的顯式原型中也沒有該屬性,因為構造函數的顯式原型也是對象,也有__proto__,那么會去它的顯式原型中查找,一直到null,如果沒有則返回undefined
2. p.__proto__.constructor == function Person(){}
3. p.___proto__.__proto__== Object.prototype
4. p.___proto__.__proto__.__proto__== Object.prototype.__proto__ == null
5. 通過__proto__形成原型鏈而非protrotype
什么是原型繼承?
Person.prototype 只是一個指針,指向的是原型對象,但是這個原型對象并不特別,它也只是一個普通對象。假設說,這時候,我們讓 Person.prototype 不再指向最初的原型對象,而是另一個類 (Animal)的實例,情況會怎樣呢?
執行該代碼 Person.prototype = new Animal() 后,Person的prototype指針指向發生了變化,指向了一個 Animal 實例。
當 p 去訪問 address 屬性時,js會先在 p 的實例屬性中查找,發現找不到后,就會去 Person 的原型對象上 查找。因為Person的原型對象已經被我們換成一個animal實例,所以就會先找animal實例的屬性,當發現還是沒有 address屬性,就會去Animal的原型對象上查找,最終找到。
這就說明,我們可以通過原型鏈的方式,實現 Person 繼承 Animal 的所有屬性和方法。
結語
看到這,相信大家對原型和原型鏈的概念應該已經有了一定了解了,如果仍然不太理解,也不用氣餒,因為閉包及原型鏈是JavaScript最難理解的幾部分。相信之后在不斷的開發實踐中會使你理解的更為透徹,多學習多思考才能更快掌握。如果大家有任何反饋和問題,也歡迎通過評論區告訴我,謝謝。
以上就是一篇文章讓你搞懂JavaScript 原型和原型鏈的詳細內容,更多關于JavaScript 原型和原型鏈的資料請關注好吧啦網其它相關文章!
相關文章:
