原型鏈以前一直都不是很理解,這兩天把《你不知道的JavaScript》和《JavaScript高級程序設計》的原型鏈那章看完後有所理解,在這裏先記下來,加深印象。前端
要講清楚什麼是原型鏈須要從原型對象開始談,那麼什麼是原型對象呢?《JavaScript高級程序設計》中是這樣講的:函數
不管何時,只要建立了一個新函數,就會根據一組特定的規則爲該函數建立一個prototype屬性,這個屬性指向函數的原型對象。學習
簡單來講,原型對象也是對象,可是經過原型對象能夠實現對象的屬性繼承。
這裏用《JavaScript高級程序設計》這本書上的demo來解釋一下:this
function Person () { } Person.prototype.name = 'Nicholas' Person.prototype.age = 29 Person.prototype.job = 'Software Engineer' Person.prototype.sayName = function () { console.log(this.name) } var person1 = new Person() var person2 = new Person()
這裏聲明瞭一個Person函數,沒有定義任何屬性;可是在Person的原型對象裏定義了name,age,job屬性和sayName方法。以後建立了兩個Person的實例對象,person1和person2。在這裏構造函數,原型對象,實例對象三者的關係用一張圖片表示就是
(圖片來源谷歌,侵刪)spa
如圖所示,Person的prototype指針指向它的原型對象,實例對象的[[Prototype]]指針也指向它的原型對象。這裏簡單說明一下什麼是[[Prototype]]指針:prototype
調用構造函數建立一個新實例以後,該實例的內部將包含一個指針(內部屬性),指向構造函數的原型對象,這就是[[Prototype]]指針。設計
如今,person1和person2的[[Prototype]]都指向了Person.prototype,這樣的話Person.prototype裏的方法和屬性是被person1和person2共用的。指針
如何證實他們共用Person.prototype裏面的屬性和方法?請執行下面的語句:code
person1.sayName() // 'Nicholas' person2.sayName() // 'Nicholas'
有人說了,你這初始化的name屬性只有一個,執行的結果固然都同樣啊。那麼請再試試下面這句:對象
console.log(person1.sayName === person2.sayName) //true
結果很明顯,person1.sayName和person2.sayName指向的是同一個方法。到這裏可能有人會疑惑:一開始的代碼中Person函數裏並無定義任何屬性和方法,爲何person1和person2能執行sayName方法?那麼這就要來談談對象屬性調用的過程了。
以上面代碼爲例,當你執行person1.name時,解析器會開始查找person1中有沒有name屬性,若是找到了則返回屬性值;若是沒找到,則在person1的原型對象中繼續找,找到了則返回屬性值;若是還沒找到,就沿着原型鏈往上繼續找,若是最終仍是沒找到就返回undefined。
到這裏你們也明白了多個對象實例共享原型對象的屬性和方法的基本原理了,那麼有人又會問了,若是我經過給實例對象屬性賦值能不能重寫原型對象裏的屬性和方法呢?
答案是不行的,在實例對象中對原型對象中的同名屬性賦值會屏蔽原型對象中的屬性。簡單解釋就是,對person1中的name屬性賦值會直接在person1中添加name屬性。可是有兩種狀況下,當name屬性不存在於person1中而存在於原型對象中時,直接給person1.name賦值會有不同事情發生:
1.當原型對象中的name屬性標記爲只讀(writable: false)時,對name屬性的賦值不會在person1添加name屬性,也不會修改原型對象中的name屬性,在嚴格模式下還會報錯。
2.當原型對象中的name屬性是一個setter,那麼對person1中的name屬性執行賦值語句就會調用setter,但name不會被添加到person1中。
這部分若是不懂什麼是隻讀和setter,你們能夠去看一下Object.defineProperty。
其實講到這裏,原型對象是什麼已經基本清楚了。那麼原型鏈就很簡單了,繼續上面的demo:
function Parent () { this.parentName = 'noOne' } function Person () { this.name = 'Nicholas' } Person.prototype = new Parent() var person1 = new Person()
這個例子中,咱們把Parent的實例對象賦給了Person的原型對象。Person.prototype中的[[Prototype]]此時指向了Parent.prototype。觸類旁通,Parent.prototype也能夠是另外一個原型的實例對象,這樣不斷地層層遞進便構成了原型鏈。
原型鏈的主要做用即是實現繼承,這部分後續的文章我會繼續講。
本人經驗尚淺,目前對於前端仍在不斷摸索和學習,文章若有錯誤,歡迎各位指正。最後附上本人博客地址和原文連接,但願能向各位多多學習。