能夠把這個(gè)講清楚弄明白是一件很困難的事,
首先明白原型是什么,在 ES6 之前,JS 沒有類和繼承的概念,JS 是通過原型來實(shí)現(xiàn)繼承的,在 JS 中一個(gè)構(gòu)造函數(shù)默認(rèn)帶有一個(gè) prototype 屬性,這個(gè)的屬性值是一個(gè)對象,同時(shí)這個(gè) prototype 對象自帶有一個(gè) constructor 屬性,這個(gè)屬性指向這個(gè)構(gòu)造函數(shù),同時(shí)每一個(gè)實(shí)例都會有一個(gè)proto屬性指向這個(gè) prototype 對象,我們可以把這個(gè)叫做隱式原型,我們在使用一個(gè)實(shí)例的方法的時(shí)候,會先檢查這個(gè)實(shí)例中是否有這個(gè)方法,沒有的話就會檢查這個(gè) prototype 對象是否有這個(gè)方法,基于這個(gè)規(guī)則,如果讓原型對象指向另一個(gè)類型的實(shí)例,即
constructor1.protoytpe=instance2,這時(shí)候如果試圖引用 constructor1 構(gòu)造的實(shí)例 instance1 的某個(gè)屬性 p1,
首先會在 instance1 內(nèi)部屬性中找一遍,
接著會在 instance1.proto(constructor1.prototype)即是 instance2 中尋找 p1
搜尋軌跡:instance1 -> instance2 -> constructor2.prototype……->Object.prototype;這即是原型鏈,原型鏈頂端是 Object.prototype
補(bǔ)充學(xué)習(xí):
每個(gè)函數(shù)都有一個(gè) prototype 屬性,這個(gè)屬性指向了一個(gè)對象,這個(gè)對象正是調(diào)用該函數(shù)而創(chuàng)建的實(shí)例的原型,那么什么是原型呢,可以這樣理解,每一個(gè) JavaScript 對象在創(chuàng)建的時(shí)候就會預(yù)制管理另一個(gè)對象,這個(gè)對象就是我們所說的原型,每一個(gè)對象都會從原型繼承屬性,如圖:

那么怎么表示實(shí)例與實(shí)例原型的關(guān)系呢,這時(shí)候就要用到第二個(gè)屬性 proto
這是每一個(gè) JS 對象都會有的一個(gè)屬性,指向這個(gè)對象的原型,如圖:

既然實(shí)例對象和構(gòu)造函數(shù)都可以指向原型,那么原型是否有屬性指向構(gòu)造函數(shù)或者實(shí)例呢,指向?qū)嵗菦]有的,因?yàn)橐粋€(gè)構(gòu)造函數(shù)可以生成多個(gè)實(shí)例,但是原型有屬性可以直接指向構(gòu)造函數(shù),通過 constructor 即可
接下來講解實(shí)例和原型的關(guān)系:
當(dāng)讀取實(shí)例的屬性時(shí),如果找不到,就會查找與對象相關(guān)的原型中的屬性,如果還查不到,就去找原型的原型,一直找到最頂層,那么原型的原型是什么呢,首先,原型也是一個(gè)對象,既然是對象,我們就可以通過構(gòu)造函數(shù)的方式創(chuàng)建它,所以原型對象就是通過 Object 構(gòu)造函數(shù)生成的,如圖:

那么 Object.prototype 的原型呢,我們可以打印 console.log(Object.prototype.proto === null),返回 true
null 表示沒有對象,即該處不應(yīng)有值,所以 Object.prototype 沒有原型,如圖:

圖中這條藍(lán)色的線即是原型鏈,
最后補(bǔ)充三點(diǎn):
constructor:
function Person(){
}
var person = new Person();
console.log(Person === person.constructor);
原本 person 中沒有 constructor 屬性,當(dāng)不能讀取到 constructor 屬性時(shí),會從 person 的原型中讀取,所以指向構(gòu)造函數(shù) Person
proto:
絕大部分瀏覽器支持這個(gè)非標(biāo)準(zhǔn)的方法訪問原型,然而它并不存在與 Person.prototype 中,實(shí)際上它來自 Object.prototype,當(dāng)使用 obj.proto時(shí),可以理解為返回來
Object.getPrototype(obj)
繼承:
前面說到,每個(gè)對象都會從原型繼承屬性,但是引用《你不知道的 JS》中的話,繼承意味著復(fù)制操作,然而 JS 默認(rèn)不會復(fù)制對象的屬性,相反,JS 只是在兩個(gè)對象之間創(chuàng)建一個(gè)關(guān)聯(lián),這樣子一個(gè)對象就可以通過委托訪問另一個(gè)對象的屬性和函數(shù),所以與其叫繼承,叫委托更合適。