【JavaScript學習筆記】理解prototype-原型

寫這個筆記是由於最近在看《JavaScript高級程序設計(第三版)》,原書寫得很是好,講解也很是細緻,甚至能夠做爲一本手冊(我少有喜歡一本手冊式的教材,不過這本絕對是個例外!)。在這本書裏學到了很多東西,不動筆墨不讀書,爲了本身仍是決定總結一些筆記出來,筆記裏的東西基本都出自書中,不過以本身的方式說出來而已。再次熱烈推薦原書,真的好贊!css

1. 原型(prototype) 是什麼呢html

若是解釋的廣義一點,應該說原型是某一類對象的共同的性質或者特徵。具體說來,在JavaScript中裏原型是定義在 function 和 對象裏的一項屬性。更明確一點說,好比每當建立了一個 function, 這個 function 內部就默認有一個 prototype 的屬性。瀏覽器

2. 原型自己是什麼類型呢?函數

原型自己實際上是一個對象,是object 。this

3. function 和 對象裏的原型有什麼不同的嗎?spa

function 的原型是能夠顯式地經過 prototype 訪問的,你能夠動態的去添加屬性,方法,你甚至能夠本身建立一個 object, 將它賦值給一個 function 的 prototype; .net

而 object 的原型是沒法經過標準方式顯式地獲得的,說得更具體一點,每當建立了一個object,不管是經過何種方式建立,這個 object 內部都會包含一個指針,這是一個內部屬性,ECMA-262第5版中管這個指針叫  [ [ Prototype ] ]  , 雖然沒有一個標準的方式去訪問它,可是在 Firefox, Chrome, Safari 裏每一個對象上都支持一個__proto__  屬性, 經過這個屬性能夠去訪問原型,而在其餘瀏覽器的實現中,這個屬性則徹底是不可見的。prototype

4. 經過 function 獲得的原型和經過對象的到的原型有什麼聯繫嗎?設計

終於到重點了,(嚴格的說,把這部份內容描述爲兩種原型的聯繫實際上是不正確的,可是當了解完這部份內容後,怎麼定義其實並不重要啦,眨眼),咱們來看示例,首先定義一個函數(構造函數),指針

 1: function Person(name, age) {
 2:     this.name = name;
 3:     this.age = age;
 4: }

這時咱們就有了一個 function,並且這個 function 會有一個 prototype 的屬性, 經過這個屬性能夠訪問該函數的原型,具體見下圖,

未命名

從圖中能夠看出,其實不只僅是能夠經過 Person.prototype 訪問原型,原型中也有一個 constructor 的屬性,Person.prototype.constructor 就指向 Person 函數自身。

接下來,咱們再建立兩個 Person 的實例,

 1: var p1 = new Person("Bob", 20);
 2: var p2 = new Person("Peter", 22);

這時候發生了什麼呢,來看看下圖,

未命名2

當建立了實例 p1 和 p2 以後,p1, p2 內都存在內部屬性 [ [ Prototype ] ], 而這個屬性,其實就指向構造函數 Person 的原型,有趣的是 p1 和 p2 和構造函數 Person 自己沒有直接的關係, 它們的聯繫就是它們都指向了同一個原型!

5. 我在構造函數的原型中添加了方法,爲何經過構造函數建立的實例也能夠直接訪問這個方法呢?

也許寫一段code能幫助理解上面的問題,

 1: Person.prototype.sayHi = function () {
 2:     alert("Hi");
 3: }
 4:  
 5: var p1 = new Person();
 6: p1.sayHi(); // why this works?

其實,每當讀取對象的某個屬性時,都會執行一次搜索,目標是具備給定名字的屬性,搜索首先從對象實例自己開始,若是在實例中找到了具備給定名字的屬性,則返回該屬性的值,若是沒有找到,則繼續搜索該實例的原型,在原型對象中查找具備給定名字的屬性

6. 若是我沒有采用構造函數的方式建立一個對象實例,那該實例的 [[ Prototype ]] 會指向什麼呢?

假若採用通常的方式建立一個對象實例,好比

 1: var obj1 = {};
 2: var obj2 = new Object();

想必你已經猜到,此時 [ [ Prototype ] ] 會指向 Object 的 prototype。

7. 當建立了一個對象實例的時候,它的默認的toString等方法藏在哪裏?

沒錯,就藏在原型裏,但不妨講得更具體一點,假設以前的 script 已經運行過,如今,咱們有了一個 Person 函數,咱們有了 Person 的實例 p1,當咱們直接調用 p1.toString() 會怎樣呢,根據問題5,咱們知道當在實例自己中搜索不到 toString 時,會轉而搜索實例的 [ [ Prototype ] ] 指向的 Person.prototype, 可是 Person.prototype 裏其實仍然沒有定義 toString ! 而後,咱們意識到其實 Person.prototype 也是一個Object!那麼繼續,再次根據問題5,Person.prototype 的[[Prototype]] 指向哪裏呢? 沒錯就是 Object 的 prototype, toString 就在這裏。

其實咱們能夠用console.dir(Person.prototype.__proto__)查看,你會發現你看到的就是Object Prototype,這裏面定義了 valueOf, toString 等等方法。

看到這裏,咱們已經能夠隱約感到JavaScript中面向對象的繼承機制了…… 原型的運用對繼承的實現相當重要!

對原型的總結就到這裏,這裏所列的僅僅是一部分,但我認爲這是理解原型中比較重要的一部分,還有一些有關原型的方法,就再也不贅述了。

相關文章
相關標籤/搜索