JavaScript 原型鏈學習(一)原型對象

在JavaScript中建立的每一個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含能夠由特定類型的全部的實例共享的屬性和方法。若是按照字面意思來理解,那麼prototype就是經過調用構造函數而建立那個實例的原型對象。使用原型對象的好處是可讓全部對象實例共享它所包含的屬性和方法。換句話說,沒必要在構造函數中定義對象實例的信息,而是能夠將這些信息直接添加到原型對象中,以下例所示:函數

function Person(){}

Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
  alert(this,name);
};

var person1 = new Person();
person1.sayName();     // "Nicholas"

var person2 = new Person();
person2.sayName();     // "Nicholas"

alert(person1.sayName == person2.sayName);    //true

本例中將sayName()方法和全部屬性直接添加到了Person的prototype屬性中,構造函數變成了空函數。即便如此,也仍然能夠經過調用構造函數來建立新對象,並且新對象還會具備相同的屬性和方法,同時這些屬性和方法是由全部實例共享的。換句話說,person1和person2訪問的都是同一組屬性和同一個sayName()函數。測試

原型對象的性質this

不管何時,只要建立了一個新函數,就會根據一組特定的規則爲該函數建立一個 prototype 屬性,這個屬性指向函數的原型對象。在默認狀況下,全部原型對象都會自動得到一個 constructor(構造函數)屬性,這個屬性是一個指向prototype 屬性所在函數的指針。就拿前面的例子來講,Person.prototype.constructor指向Person。而經過這個構造函數,咱們還可繼續爲原型對象添加其餘屬性和方法。spa

建立了自定義的構造函數以後,其原型對象默認只會取得constructor屬性;至於其餘方法,則都是從Object繼承而來的。當調用構造函數建立一個新實例後, 該實例的內部將包含一個指針 (內部屬性)指向構造函數的原型對象。ECMA-262第5版中管這個指針叫[[Prototype]]。雖然在腳本中沒有標準的方式訪問 [[Prototype]] 但Firefox、Safari 和Chome在每一個對象上都支持一個屬性 __proto__ ;而在其餘實現中,這個屬性對腳本則是徹底不可見的。不過,要明確的真正重要的一點就是,這個鏈接存在於實例構造函數的原型對象之間,而不是存在於實例與構造函數之間。prototype

用上例中使用的 Person 構造函數和 Person.prototype 建立實例的代碼爲例,下圖展現了各個對象之間的關係。指針

 

上圖展現了Person構造函數Person 的原型屬性以及Person現有的兩個實例之間的關係。在此,Person.prototype指向了原型對象,而Person.prototype.constructor又指回了Person。原型對象中除了包含constructor屬性以外,還包括後來添加的其餘屬性。 Person的每一個實例——person1和person2都包含一個內部屬性該屬性僅僅指向了Person.prototype;換句話說,它們與構造函數沒有直接的關係。此外,要格外注意的是,雖然這兩個實例都不包含屬性和方法,但咱們卻能夠調用person1.sayName()。這是經過查找對象屬性的過程來實現的。 code

雖然在全部實現中都沒法訪問到[[Prototype]],但能夠經過isPrototypeOf()方法來肯定對象之間是否存在這種關係。從本質上講,若是[[Prototype]]指向調用isPrototypeOf()方法的對象(Person.prototype),那麼這個方法就返回true,以下所示:對象

alert(Person.prototype.isPrototypeOf(person1));  / /true
alert(Person.prototype.isPrototypeOf(person2));  / /true

這裏,用原型對象的isPrototypeOf()方法測試了person1  和person2。 由於它們內部都有一個指向Person.prototype的指針,所以都返回了true。blog

每當代碼讀取某個對象的某個屬性時,都會執行一次搜索,目標是具備給定名字的屬性。搜索首先從對象實例自己開始。若是在實例中找到了具備給定名字的屬性,則返回該屬性的值;若是沒有找到,則繼續搜索指針指向的原型對象,在原型對象中查找具備給定名字的屬性。若是在原型對象中找到了這個屬性,則返回該屬性的值。也就是說,在咱們調用person1.sayName()的時候,會前後執行兩次搜索。首先,解析器會問:「實例person1有sayName屬性嗎?」 答:「沒有。」 而後,它繼續搜索,再問:「person1 的原型有sayName屬性嗎?」 答:「有。」 因而,它就讀取那個保存在原型對象中的函數。當咱們調用person2.sayName()時,將會重現相同的搜索過程,獲得相同的結果。而這正是多個對象實例共享原型所保存的屬性和方法的基本原理。繼承

雖然能夠經過對象實例訪問保存在原型中的值,但卻不能經過對象實例重寫原型中的值。若是咱們在實例中添加了一個屬性,而該屬性與實例原型中的一個屬性同名,那咱們就在實例中建立該屬性,該屬性將會屏蔽原型中的那個屬性。

相關文章
相關標籤/搜索