JavaScript原型及原型鏈

一、原型

原型是 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 對象一樣。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容