引用: javaScript是一門基於原型的語言,它容許對象經過原型鏈引用另外一個對象來構建對象中的複雜性,JavaScript使用原型鏈這種機制來實現動態代理。當試圖去引用某一個屬性時,它會遍歷整個原型鏈,直到最後的節點。JavaScript專家編程·P24java
在JavaScript中除了基本數據類型外的其它數據都是對象類型,包括對象、函數、數組等,它們跟原型對象密不可分。編程
JavaScript語言中有一個很是重要的概念,叫作原型對象
,理解原型對象是進一步理解這門語言的基礎,由於它是一門基於原型的語言,也由於全部的代碼幾乎都和原型對象有關,接下來咱們先了解下原型對象是什麼。數組
原型對象瀏覽器
在上一篇文章JavaScript系列 [02]-javaScript對象探析中,咱們介紹了使用自定義構造函數建立對象的方式,在構造函數被建立出來的時候,系統會默認幫構造函數建立並關聯一個Object類型的新對象,咱們稱該對象就是這個構造函數的原型對象,構造函數的原型對象默認是一個空對象。函數
構造函數相關聯的原型對象的成員(屬性和方法),能夠被使用該構造函數建立出來的對象訪問,即以自定義構造函數方式建立出來的全部實例對象,都自動擁有和共享該構造函數原型對象中的全部屬性和方法(想想爲何空對象可使用toString方法,全部的數組均可以使用push等方法
)。post
代碼示例this
1 //01 聲明構造函數Person 2 function Person(name) { 3 this.name = name; 4 } 5 //02 打印構造函數相關聯的原型對象 6 console.log(Person.prototype); //Objec類型的空對象 7 //03 給構造函數原型對象添加方法 8 Person.prototype.showName = function () { 9 console.log(this.name); 10 }; 11 //04 使用構造函數建立實例對象 12 var p = new Person("文頂頂"); 13 p.showName(); //文頂頂 14 console.log(p);
代碼說明spa
☞ 上面的代碼先提供了Person構造函數,該函數聲明後,咱們經過Person.prototype訪問其原型對象打印獲得一個Object類型的空對象,說明全部的構造函數建立後默認擁有prototype屬性,即構造函數默認有一個相關聯的原型對象(Object類型空對象)。prototype
☞ 隨後咱們經過對象的動態特性給Person的原型對象添加了showName方法,經過打印結果能夠驗證構造函數的實例化對象(p)能夠訪問其原型對象上面的成員。代理
經過對代碼和其運行結果分析,咱們不可貴出構造函數(Person)、原型對象(Person.prototype)、實例對象(p)之間的關係圖,以下。
代碼說明
① 實例對象p由Person構造函數實例化而來。
② Person構造函數能夠經過prototype屬性訪問其原型對象。
③ 實例對象p能夠經過proto屬性訪問其構造函數的原型對象(能夠簡稱爲p的原型對象,咱們在說原型對象的時候,應該先肯定主語是構造函數仍是實例對象,若是主語是構造函數,那麼指的是構造函數.prototype,若是主語是實例對象,那麼指的是建立該實例對象的構造函數相關聯的原型對象,表示爲實例對象.proto)。
④ 原型對象(Person.prototype)能夠經過constructor(構造器)屬性來訪問其關聯的構造函數,沒法訪問實例對象。
下面貼出上面代碼更詳細的原型結構關係圖。
原型對象的訪問
1 //獲取原型對象的方式 2 //01 構造函數訪問原型對象:構造函數.prototype 3 console.log(Person.prototype); 4 //02 構造函數的實例對象訪問原型對象:實例對象.__proto__ 5 console.log(p.__proto__); 6 console.log(p.__proto__ == Person.prototype); 7 //03 經過Object.getPrototypeOf方法傳遞實例對象做爲參數訪問 8 console.log(Object.getPrototypeOf(p));
總結一下,原型對象的訪問方式以下
① 構造函數.prototype
② 實例對象.__proto__
③ Object.getPrototypeOf(實例對象)
原型對象總結
❐ 全部的對象都擁有
__proto__
屬性,函數既擁有prototype屬性又擁有__proto__
屬性。
❐ 對象的__proto__
屬性指向其構造函數相關聯的原型對象(函數的__proto__屬性也同樣,指向其構造函數Function相關的原型對象,是一個空函數
)。
❐ 函數的prototype屬性指向默認相關聯的原型對象(函數和構造函數本質無差異)。
__proto__
是一個非標準屬性,即ECMAScript中並不包含該屬性,這只是某些瀏覽器爲了方便開發人員開發和調試而提供的一個屬性,不具有通用性。建議在調試的時候可使用該屬性,但不能出如今正式的代碼中,開發中可使用Object.getPrototypeOf方法來替代。
所謂設置原型對象就是給構造函數的原型對象添加成員(屬性和方法),具體的方式有兩種
① 利用對象的動態特性設置
② 替換原有的原型對象
代碼示例
1 //01 聲明構造函數Person 2 function Person(name,age) { 3 this.name = name; 4 this.age = age || 18; 5 } 6 //02 給構造函數原型對象添加方法 7 //設置原型對象的第一種方法 8 Person.prototype.showName = function () { 9 console.log("姓名 "+this.name); 10 }; 11 Person.prototype.showAge = function () { 12 console.log("年齡 "+this.age); 13 }; 14 //04 使用構造函數建立實例對象 15 var p1 = new Person("文頂頂"); 16 p1.showName(); //姓名 文頂頂 17 p1.showAge(); //年齡 18 18 var p2 = new Person("章飛一絕",99); 19 p2.showName(); //姓名 章飛一絕 20 p2.showAge(); //年齡 99
像上面代碼這樣直接利用對象的動態特性來設置原型對象,在原有原型對象的基礎上添加屬性和方法很是簡單,可是若是要添加的方法或屬性比較多,那麼冗餘代碼會比較多,這種狀況推薦直接替換原有的原型對象。
1 //01 聲明構造函數Person 2 function Person(name,age) { 3 this.name = name; 4 this.age = age || 18; 5 } 6 //02 給構造函數原型對象添加方法 7 //設置原型對象的第二種方法:直接替換原先的原型對象 8 Person.prototype = { 9 constructor:Person, 10 showName:function () { 11 console.log("姓名 "+this.name); 12 }, 13 showAge:function () { 14 console.log("年齡 "+this.age); 15 } 16 }; 17 //04 使用構造函數建立實例對象 18 var p = new Person("文頂頂"); 19 p.showName(); //姓名 文頂頂 20 p.showAge(); //年齡 18 21 console.log(p.constructor); //Person函數
注意
若是是直接替換原型對象,那麼須要修正構造器屬性,讓constructor指向構造函數。說明
由於替換的時候實際上是用字面量的方式從新建立了新的對象,該對象做爲Object構造函數的原型對象,內部沒有constructor屬性。這個時候若是要經過實例對象(好比p)的構造器屬性判斷其類型,那麼會先在p身上找,沒有則查找原型對象發現也沒有,最終獲得的Object.prototype身上的構造器屬性,結果爲Object 。