能夠把這個(gè)講清楚弄明白是一件很困難的事拥坛,
首先明白原型是什么蓬蝶,在 ES6 之前,JS 沒有類和繼承的概念猜惋,JS 是通過原型來實(shí)現(xiàn)繼承的丸氛,在 JS 中一個(gè)構(gòu)造函數(shù)默認(rèn)帶有一個(gè) prototype 屬性,這個(gè)的屬性值是一個(gè)對(duì)象著摔,同時(shí)這個(gè) prototype 對(duì)象自帶有一個(gè) constructor 屬性缓窜,這個(gè)屬性指向這個(gè)構(gòu)造函數(shù),同時(shí)每一個(gè)實(shí)例都會(huì)有一個(gè)proto屬性指向這個(gè) prototype 對(duì)象谍咆,我們可以把這個(gè)叫做隱式原型禾锤,我們?cè)谑褂靡粋€(gè)實(shí)例的方法的時(shí)候,會(huì)先檢查這個(gè)實(shí)例中是否有這個(gè)方法摹察,沒有的話就會(huì)檢查這個(gè) prototype 對(duì)象是否有這個(gè)方法恩掷,基于這個(gè)規(guī)則,如果讓原型對(duì)象指向另一個(gè)類型的實(shí)例供嚎,即
constructor1.protoytpe=instance2黄娘,這時(shí)候如果試圖引用 constructor1 構(gòu)造的實(shí)例 instance1 的某個(gè)屬性 p1,
首先會(huì)在 instance1 內(nèi)部屬性中找一遍,
接著會(huì)在 instance1.proto(constructor1.prototype)即是 instance2 中尋找 p1
搜尋軌跡:instance1 -> instance2 -> constructor2.prototype……->Object.prototype;這即是原型鏈克滴,原型鏈頂端是 Object.prototype
補(bǔ)充學(xué)習(xí):
每個(gè)函數(shù)都有一個(gè) prototype 屬性逼争,這個(gè)屬性指向了一個(gè)對(duì)象,這個(gè)對(duì)象正是調(diào)用該函數(shù)而創(chuàng)建的實(shí)例的原型偿曙,那么什么是原型呢氮凝,可以這樣理解,每一個(gè) JavaScript 對(duì)象在創(chuàng)建的時(shí)候就會(huì)預(yù)制管理另一個(gè)對(duì)象望忆,這個(gè)對(duì)象就是我們所說的原型罩阵,每一個(gè)對(duì)象都會(huì)從原型繼承屬性,如圖:
那么怎么表示實(shí)例與實(shí)例原型的關(guān)系呢启摄,這時(shí)候就要用到第二個(gè)屬性 proto
這是每一個(gè) JS 對(duì)象都會(huì)有的一個(gè)屬性稿壁,指向這個(gè)對(duì)象的原型,如圖:
既然實(shí)例對(duì)象和構(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í)帽驯,如果找不到,就會(huì)查找與對(duì)象相關(guān)的原型中的屬性书闸,如果還查不到尼变,就去找原型的原型,一直找到最頂層浆劲,那么原型的原型是什么呢嫌术,首先,原型也是一個(gè)對(duì)象牌借,既然是對(duì)象度气,我們就可以通過構(gòu)造函數(shù)的方式創(chuàng)建它,所以原型對(duì)象就是通過 Object 構(gòu)造函數(shù)生成的膨报,如圖:
那么 Object.prototype 的原型呢磷籍,我們可以打印 console.log(Object.prototype.proto === null),返回 true
null 表示沒有對(duì)象丙躏,即該處不應(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í)汪诉,會(huì)從 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è)對(duì)象都會(huì)從原型繼承屬性迄本,但是引用《你不知道的 JS》中的話,繼承意味著復(fù)制操作课竣,然而 JS 默認(rèn)不會(huì)復(fù)制對(duì)象的屬性嘉赎,相反,JS 只是在兩個(gè)對(duì)象之間創(chuàng)建一個(gè)關(guān)聯(lián)于樟,這樣子一個(gè)對(duì)象就可以通過委托訪問另一個(gè)對(duì)象的屬性和函數(shù)公条,所以與其叫繼承,叫委托更合適迂曲。