一、原型
原型是 ECMAScript 實現繼承的過程中產生的一個概念。
繼承:
java 中:指在已有的一個類基礎上創建新類的過程。
ES:指在一個對象的基礎上創建新對象的過程。原型指在這過程中作為基礎的對象。
創建原型
// 鳥對象
var bird = {
? ? name: "bird",
? ? fly: function () {
? ? ? ? console.log("fly");
? ? }
}
假設我們需要一個鷹對象,因為我們已經有一個鳥對象,因此可以從這個鳥對象繼承信息。
//鷹對象
var eagle = Object.create(bird);
eagle.fly(); // fly
通過 Object.create() 方法我們傳入了鳥對象,作為鷹對象的原型來創建鷹對象,然后鷹對象中就產生了一個叫 _proto_ 的指針,這指針指向鳥對象。通過這個指針鷹對象就可以訪問到鳥對象的 fly() 方法,當然編譯器幫我們自動處理了這個指針訪問的過程。
但是對于原型來講,prototype 屬性是很重要的存在,下面來講講 prototype。
二、prototype 屬性
從一個例子講 prototype 屬性存在解決了什么問題。
現在有一個叫做 DOG 的構造函數,表示狗對象的原型。
function DOG(name){
? ? this.name = name;
}
對這個構造函數使用new,就會生成一個狗對象的實例。
var dogA = new DOG('大毛');
alert(dogA.name); // 大毛
new運算符的缺點
但是,用構造函數生成實例對象,有一個缺點,那就是無法共享屬性和方法。
比如,在DOG對象的構造函數中,設置一個實例對象的共有屬性species。
function DOG(name){
? ? this.name = name;
? ? this.species = '犬科';
}
然后,生成兩個實例對象:
var dogA = new DOG('大毛');
var dogB = new DOG('二毛');
這兩個對象的species屬性是獨立的,修改其中一個,不會影響到另一個。
dogA.species = '貓科';
alert(dogB.species); // 顯示"犬科",不受dogA的影響
每一個實例對象,都有自己的屬性和方法的副本。這不僅無法做到數據共享,也是極大的資源浪費。
prototype屬性的引入
考慮到這一點,Brendan Eich 決定為構造函數設置一個 prototype 屬性。
這個屬性包含一個對象(以下簡稱” prototype 對象”),所有實例對象需要共享的屬性和方法,都放在這個對象里面;那些不需要共享的屬性和方法,就放在構造函數里面。
實例對象一旦創建,將自動引用 prototype 對象的屬性和方法。也就是說,實例對象的屬性和方法,分成兩種,一種是本地的,另一種是引用的。
還是以 DOG 構造函數為例,現在用 prototype 屬性進行改寫:
function DOG(name){
? ? this.name = name;
}
DOG.prototype = { species : '犬科' };
var dogA = new DOG('大毛');
var dogB = new DOG('二毛');
alert(dogA.species); // 犬科
alert(dogB.species); // 犬科
現在,species屬性放在prototype對象里,是兩個實例對象共享的。只要修改了prototype對象,就會同時影響到兩個實例對象。
DOG.prototype.species = '貓科';
alert(dogA.species); // 貓科
alert(dogB.species); // 貓科
三、原型鏈
講原型一個不可避免的概念就是原型鏈,原型鏈是通過前面兩種創建原型的方式 Object.create() 或 DOG.prototype 時生成的一個 _proto_ 指針來實現的。
以 DOG 為例講原型鏈
紅色的箭頭就是原型鏈。DOG 對象有一個 prototype 對象,而實例對象 dogA 通過一個 _proto_ 對象引用這個 prototype 對象。
可以看出 dogA 能訪問到的 species 屬性實際上是在 DOG 的原型電源線 prototype 中,因此才能實現實例對象屬性共享訪問卻不能修改。
但是在 DOG.prototype 中還存在一個 _proto_ 屬性,這又是指向誰呢?
看圖
指向 Object 對象,這樣 DOG 對象就擁有 Object 對象中原型屬性和方法。比如說 toString() 就在其中。
還有一點,那就是 Js 的函數也是對象啊,我們每個創建的函數其實也繼承了一個函數對象,而函數則繼承了 Object 對象。。
以上就是一個簡單的 Dog 類完整的原型鏈。
總結一下原型鏈作用:對象屬性的訪問修改和刪除。
訪問。優先在對象本身查找,沒有則順著原型鏈向上查找
修改。只能修改跟刪除自身屬性,不會影響到原型鏈上的其他對象。
四、總結
由于所有的實例對象共享同一個 prototype 對象,那么從外界看起來,prototype 對象就好像是實例對象的原型,而實例對象則好像”繼承”了 prototype 對象一樣。