以前對js原型和原型鏈的理解一直以爲很繞,繞來繞去的,在看了《JavaScript高級程序設計》和各類文章以後,終於對原型和原型鏈有了初步的瞭解,但是仍是沒有很深刻的瞭解,今次經過之前段時間遇到的一道題,分析一下,用本身的想法進行解讀,加深本身對原型和原型鏈的理解。函數
下面程序運行結果是什麼?this
function Animal() { this.name = 'Animal'; } Animal.prototype.changeName = function (name) { this.name = name; } function Cat() { this.name = 'Cat'; } var animal = new Animal(); Cat.prototype = animal; Cat.prototype.constructor = Cat; var cat = new Cat(); animal.changeName('Tiger'); console.log(cat.name)
A. Animal
B. Cat
C. Tiger
D. 都不是spa
答案是 B Catprototype
不管何時,只要建立了一個新函數,就會根據一組特定的規則爲該函數建立一個prototype
屬性,這個屬性指向函數的原型對象。在默認狀況下,全部的原型對象都會自動得到一個constructor
(構造函數)屬性,這個屬性是一個指向prototype
屬性所在函數的指針。
下面用圖來講明
function Animal() { this.name = 'Animal'; } Animal.prototype.changeName = function (name) { this.name = name; }
首先建立了一個Animal函數,Animal中含有一個prototype
屬性,指向Animal Prototype,而Animal.prototype.constructor
指向Animal。這個時候因爲name
屬性是在函數中定義的,因此不在Animal Prototype中,而changeName
函數是經過Animal.prototype.changeName
定義的,因此咱們能夠經過這種方式,在實例化多個對象時,共享原型所保存的方法。
同理,當建立了Cat函數時,也是同樣。設計
function Cat() { this.name = 'Cat'; }
當調用構造函數建立一個新實例後,該實例的內部將包含一個指針(內部屬性),指向構造函數的原型對象。在ECMA-262第5版中管這個指針叫[[Prototype]]
。雖然在腳本中沒有標準的方式訪問[[Prototype]]
,但Firefox、Safari和Chrome在每一個對象上都支持一個屬性__proto__
。明確重要的一點,這個鏈接存在於實例與構造函數的原型對象之間,而不是存在於實例與構造函數之間。
// 將Cat的原型對象指向animal實例,得到animal中的屬性,原有的屬性丟失 Cat.prototype = animal;
這一部分至關因而把Cat的原型對象的指針指向了animal
實例,因此原來Cat原型對象中的constructor
屬性丟失,替換成了animal
實例中的屬性,包括name
屬性以及__proto__
內部屬性,同時__proto__
屬性也指向Animal.prototype
,所以Cat也能夠經過原型鏈查找調用到Animal中的屬性和方法。3d
// 至關於從新建立了constructor,指向Cat構造函數 Cat.prototype.constructor = Cat;
這一部分至關因而從新在原型對象中建立了一個constructor
屬性,同時指向Cat構造函數。指針
var cat = new Cat(); // 實例化一個Cat對象,跟實例化Animal類似
animal.changeName('Tiger');
當var animal = new Animal();
實例化了一個Animal對象後,animal
都包含一個內部屬性,該屬性指向了Animal.prototype
;換句話說,animal
與構造函數Animal沒有直接的關係。但是,能夠看到雖然在實例中不含changeName
,但咱們卻能夠調用animal.changeName(name)
,這是經過查找對象屬性的過程來實現的,即:code
首先查找實例中實例中animal
是否有changeName
方法,若是沒有則繼續尋找,去到Animal.prototype
尋找是否有changeName
方法,若是有則調用,沒有則繼續尋找,到Object.prototype
中尋找,最後沒找到則會返回一個null
。對象
很明顯,在這裏實例animal
中沒有changeName
方法,因此須要到Animal.prototype
尋找changeName
方法,並調用成功修改了實例animal
中的name
屬性,爲Tiger
。blog
這個時候因爲Cat.prototype
是指向實例animal
的,所以Cat.prototype
中的name
屬性也變爲Tiger
。
console.log(cat.name) // Cat
最後,獲取cat.name
,與查找方法一樣,也是先去實例中cat
查找是否含有name
屬性,在這裏很明顯是存在的,所以直接結束尋找,此時cat.name = 'Cat'
。
經過這道題,加深了我對原型和原型鏈的理解,其實這道題也能夠擴展到關於繼承的知識點,在JavaScript中實現繼承主要是依靠原型鏈來實現。以後等我再搞的更清楚一點再繼續寫吧。
此文章是本人本身對於原型和原型鏈的一點小小的理解,中間可能存在誤差或者錯誤的,請多多指點!!!