天天看點

對Javascript中原型的深入了解

  了解原型對象

在Javascript中無論什麼時候,隻要建立一個新的函數,就會根據一組特定的規則為該函數建立一個prototype屬性,這個屬性指向函數的原型對象(這個對象的用途是包含可以有特定類型的所有執行個體共享的屬性和方法)。如果按照字面上的意思來了解,那麼原型屬性就是通過調用構造函數而建立的那個對象的執行個體的原型對象。

在預設的情況下,所有的原型對象都會自動獲得一個constructor(構造函數)屬性,這個屬性包含一個指向prototype屬性所在函數的指針。如下圖

對Javascript中原型的深入了解

上圖中建立了一個構造函數Person(function Person()),Person函數中有一個prototype的屬性指向Person的原型對象,在原型對象中有一個constructor的屬性指向了Person函數,所有可以通過new Person()建立對象。

建立了自定義的構造函數後,其原型對象預設隻會取得constructor屬性,至于其他的方法,都是從Object繼承而來的。其中上圖中Person.prototype .constructor指向Person.而通過這個構造函數,我們可以繼續為原型對象添加其他的屬性和方法。如下圖:

對Javascript中原型的深入了解

上圖的js代碼就是

當調用構造函數建立一個新執行個體後,該執行個體的内部将包含一個指針(内部屬性),指向構造函數的原型對象。ECMA-262第5版中管這個指針叫[[Prototype]],雖然在腳本中沒有标準方式通路,我們可以借由Chrome在每個對象上都支援一個屬性_proto_(私有屬性不可見),這個連結的存在于執行個體與構造函數的原型對象之間,而不是存在于執行個體與構造函數之間。

對Javascript中原型的深入了解

var p1=new Person();

當建立一個新的p2之後依然會有一個_proto_屬性指向Person的原型(Persion.prototype),換句話說每個執行個體與構造函數沒有直接的關系,雖然這執行個體都不包含屬性和方法,但我們卻可以調用p1.name查找對象屬性的個過程來實作。此時如果通過p2.name設定屬性值後會在對象自己的記憶體空間中存儲name的值,當調用sayName()方法的時候在尋找name時,在自己空間中找到之後就不會去原型對象中查找(注意:原型中的值是不會被替換,僅僅是在查找的時候被覆寫)

對Javascript中原型的深入了解

原型的檢測

在所有實作中都無法通路到[[Prototype]],但是我們可以通過isPrototypeOf()方法來确定對象直接是否存在這種關系。從本質上講,如果[[Prototype]]指向調用isPrototypeOf()方法的對象(Person.prototype)。

ECMAScript5中增加一個新的方法.叫做Object.getPrototypeOf(),這個方法傳回[[Prototype]]的值

== 可以檢測某個對象的constructor

檢測某個屬性是否是自己是自己的屬性用(不是對象原型中的實作)hasOwnProperty(),可以清楚的知道什麼時候通路的自己的屬性,什麼時候通路的是原型屬性了。

delete可以用來删除執行個體屬性(自己的屬性)

in操作:有兩種方式操作in操作符。單獨的使用for-in循環中使用。單獨是用時,in

會通過對象能夠通路給的屬性的傳回值,無論屬性存在于執行個體中還是原型中。

在上面給原型添加的屬性中存在着已問題constructor屬性不指向Person了。上面寫法的本質是完全重寫了預設的prototype對象,是以constructor屬性也變成了新的對象的constructor屬性(指向Object構造函數),不在指向Person函數,盡管instanceof操作還是正确傳回結果,但是通過constructor已經無法确定對象的類型了。

   為了避免這個問題可是手動指定constructor

alert(p1 instanceofPersion);alert(p1.constructor==Persion)

原型重寫存在的問題

 由于在原型中查找值得過程是一次搜尋,我們對原型對象所做的任何修改都能夠立即從執行個體上反映出來,即使先建立執行個體後修改原型也是可以的

可以擷取值是因為執行個體與原型之間的松散連接配接關系,當我們調用p2.saHi()時,首先回去執行個體中搜尋名為sayHi的屬性,在沒有找到的情況下,會繼續搜尋原型,因為執行個體和原型之間隻不過是一個指針,而不是副本,是以可以在原型中找到新的sayHi屬性并傳回儲存在哪裡的函數。

如果是重寫整個原型就不一樣了。我們知道調用構造函數是會為執行個體添加一個指向最初原型的[[Prototype]]指針。把原型修改為另外一個對象就等于切斷了構造函數和最初的原型之間的聯系。切記:執行個體中的指針僅指向原型。而不是構造函數。

重寫之前與重寫之後如圖:

對Javascript中原型的深入了解

原型存在的問題

原型對象中的屬性是被很多執行個體共享的,這個共享對于函數是非常合适的,對于原型中基本值得屬性是沒有問題的但是,如果屬性值包含引用類型值就會有一定的問題了。

解決這個為題做好的方法如下:

繼續閱讀