[技術分享] JavaScript原型及原型鏈

 作者:新投云  發布于:2017-07-04  瀏覽數:

一、原型

原型是 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 為例講原型鏈

img

紅色的箭頭就是原型鏈。DOG 對象有一個 prototype 對象,而實例對象 dogA 通過一個 _proto_ 對象引用這個 prototype 對象。

可以看出 dogA 能訪問到的 species 屬性實際上是在 DOG 的原型電源線 prototype 中,因此才能實現實例對象屬性共享訪問卻不能修改。

但是在 DOG.prototype 中還存在一個 _proto_ 屬性,這又是指向誰呢?

看圖
img
指向 Object 對象,這樣 DOG 對象就擁有 Object 對象中原型屬性和方法。比如說 toString() 就在其中。

還有一點,那就是 Js 的函數也是對象啊,我們每個創建的函數其實也繼承了一個函數對象,而函數則繼承了 Object 對象。。
image

以上就是一個簡單的 Dog 類完整的原型鏈。

總結一下原型鏈作用:對象屬性的訪問修改和刪除。

  1. 訪問。優先在對象本身查找,沒有則順著原型鏈向上查找
  2. 修改。只能修改跟刪除自身屬性,不會影響到原型鏈上的其他對象。

四、總結

由于所有的實例對象共享同一個 prototype 對象,那么從外界看起來,prototype 對象就好像是實例對象的原型,而實例對象則好像”繼承”了 prototype 對象一樣。

相關文章

三期必中一期平特肖