引用: javaScript是一門基于原型的語言,它允許對(duì)象通過原型鏈引用另一個(gè)對(duì)象來構(gòu)建對(duì)象中的復(fù)雜性,JavaScript使用原型鏈這種機(jī)制來實(shí)現(xiàn)動(dòng)態(tài)代理。當(dāng)試圖去引用某一個(gè)屬性時(shí),它會(huì)遍歷整個(gè)原型鏈,直到最后的節(jié)點(diǎn)。JavaScript專家編程·P24
1.1 原型對(duì)象說明
在JavaScript中除了基本數(shù)據(jù)類型外的其它數(shù)據(jù)都是對(duì)象類型,包括對(duì)象、函數(shù)、數(shù)組等,它們跟原型對(duì)象密不可分。
JavaScript語言中有一個(gè)非常重要的概念,叫做原型對(duì)象
,理解原型對(duì)象是進(jìn)一步理解這門語言的基礎(chǔ),因?yàn)樗且婚T基于原型的語言,也因?yàn)樗械拇a幾乎都和原型對(duì)象有關(guān),接下來我們先了解下原型對(duì)象是什么。
原型對(duì)象
在上一篇文章JavaScript系列 [02]-javaScript對(duì)象探析中,我們介紹了使用自定義構(gòu)造函數(shù)創(chuàng)建對(duì)象的方式,在構(gòu)造函數(shù)被創(chuàng)建出來的時(shí)候,系統(tǒng)會(huì)默認(rèn)幫構(gòu)造函數(shù)創(chuàng)建并關(guān)聯(lián)一個(gè)Object類型的新對(duì)象,我們稱該對(duì)象就是這個(gè)構(gòu)造函數(shù)的原型對(duì)象,構(gòu)造函數(shù)的原型對(duì)象默認(rèn)是一個(gè)空對(duì)象。
1.2 原型對(duì)象的性質(zhì)
構(gòu)造函數(shù)相關(guān)聯(lián)的原型對(duì)象的成員(屬性和方法),可以被使用該構(gòu)造函數(shù)創(chuàng)建出來的對(duì)象訪問,即以自定義構(gòu)造函數(shù)方式創(chuàng)建出來的所有實(shí)例對(duì)象,都自動(dòng)擁有和共享該構(gòu)造函數(shù)原型對(duì)象中的所有屬性和方法(想一想為什么空對(duì)象可以使用toString方法,所有的數(shù)組都可以使用push等方法
)。
代碼示例
//01 聲明構(gòu)造函數(shù)Person
function Person(name) {
this.name = name;
}
//02 打印構(gòu)造函數(shù)相關(guān)聯(lián)的原型對(duì)象
console.log(Person.prototype); //Objec類型的空對(duì)象
//03 給構(gòu)造函數(shù)原型對(duì)象添加方法
Person.prototype.showName = function () {
console.log(this.name);
};
//04 使用構(gòu)造函數(shù)創(chuàng)建實(shí)例對(duì)象
var p = new Person("文頂頂");
p.showName(); //文頂頂
console.log(p);
代碼說明
? 上面的代碼先提供了Person構(gòu)造函數(shù),該函數(shù)聲明后,我們通過Person.prototype訪問其原型對(duì)象打印得到一個(gè)Object類型的空對(duì)象,說明所有的構(gòu)造函數(shù)創(chuàng)建后默認(rèn)擁有prototype屬性,即構(gòu)造函數(shù)默認(rèn)有一個(gè)相關(guān)聯(lián)的原型對(duì)象(Object類型空對(duì)象)。
? 隨后我們通過對(duì)象的動(dòng)態(tài)特性給Person的原型對(duì)象添加了showName方法,通過打印結(jié)果可以驗(yàn)證構(gòu)造函數(shù)的實(shí)例化對(duì)象(p)可以訪問其原型對(duì)象上面的成員。
通過對(duì)代碼和其運(yùn)行結(jié)果分析,我們不難得出構(gòu)造函數(shù)(Person)、原型對(duì)象(Person.prototype)、實(shí)例對(duì)象(p)之間的關(guān)系圖,如下。
代碼說明
① 實(shí)例對(duì)象p由Person構(gòu)造函數(shù)實(shí)例化而來。
② Person構(gòu)造函數(shù)可以通過prototype屬性訪問其原型對(duì)象。
③ 實(shí)例對(duì)象p可以通過proto屬性訪問其構(gòu)造函數(shù)的原型對(duì)象(可以簡稱為p的原型對(duì)象,我們?cè)谡f原型對(duì)象的時(shí)候,應(yīng)該先確定主語是構(gòu)造函數(shù)還是實(shí)例對(duì)象,如果主語是構(gòu)造函數(shù),那么指的是構(gòu)造函數(shù).prototype,如果主語是實(shí)例對(duì)象,那么指的是創(chuàng)建該實(shí)例對(duì)象的構(gòu)造函數(shù)相關(guān)聯(lián)的原型對(duì)象,表示為實(shí)例對(duì)象.proto)。
④ 原型對(duì)象(Person.prototype)可以通過constructor(構(gòu)造器)屬性來訪問其關(guān)聯(lián)的構(gòu)造函數(shù),無法訪問實(shí)例對(duì)象。
下面貼出上面代碼更詳細(xì)的原型結(jié)構(gòu)關(guān)系圖。
原型對(duì)象的訪問
//獲取原型對(duì)象的方式
//01 構(gòu)造函數(shù)訪問原型對(duì)象:構(gòu)造函數(shù).prototype
console.log(Person.prototype);
//02 構(gòu)造函數(shù)的實(shí)例對(duì)象訪問原型對(duì)象:實(shí)例對(duì)象.__proto__
console.log(p.__proto__);
console.log(p.__proto__ == Person.prototype);
//03 通過Object.getPrototypeOf方法傳遞實(shí)例對(duì)象作為參數(shù)訪問
console.log(Object.getPrototypeOf(p));
總結(jié)一下,原型對(duì)象的訪問方式如下
① 構(gòu)造函數(shù).prototype
② 實(shí)例對(duì)象.proto
③ Object.getPrototypeOf(實(shí)例對(duì)象)
原型對(duì)象總結(jié)
? 所有的對(duì)象都擁有
__proto__
屬性,函數(shù)既擁有prototype屬性又擁有__proto__
屬性。
? 對(duì)象的__proto__
屬性指向其構(gòu)造函數(shù)相關(guān)聯(lián)的原型對(duì)象(函數(shù)的__proto__屬性也一樣,指向其構(gòu)造函數(shù)Function相關(guān)的原型對(duì)象,是一個(gè)空函數(shù)
)。
? 函數(shù)的prototype屬性指向默認(rèn)相關(guān)聯(lián)的原型對(duì)象(函數(shù)和構(gòu)造函數(shù)本質(zhì)無差別)。
__proto__
是一個(gè)非標(biāo)準(zhǔn)屬性,即ECMAScript中并不包含該屬性,這只是某些瀏覽器為了方便開發(fā)人員開發(fā)和調(diào)試而提供的一個(gè)屬性,不具備通用性。建議在調(diào)試的時(shí)候可以使用該屬性,但不能出現(xiàn)在正式的代碼中,開發(fā)中可以使用Object.getPrototypeOf方法來替代。
1.3 設(shè)置原型對(duì)象
所謂設(shè)置原型對(duì)象就是給構(gòu)造函數(shù)的原型對(duì)象添加成員(屬性和方法),具體的方式有兩種
① 利用對(duì)象的動(dòng)態(tài)特性設(shè)置
② 替換原有的原型對(duì)象
代碼示例
//01 聲明構(gòu)造函數(shù)Person
function Person(name,age) {
this.name = name;
this.age = age || 18;
}
//02 給構(gòu)造函數(shù)原型對(duì)象添加方法
//設(shè)置原型對(duì)象的第一種方法
Person.prototype.showName = function () {
console.log("姓名 "+this.name);
};
Person.prototype.showAge = function () {
console.log("年齡 "+this.age);
};
//04 使用構(gòu)造函數(shù)創(chuàng)建實(shí)例對(duì)象
var p1 = new Person("文頂頂");
p1.showName(); //姓名 文頂頂
p1.showAge(); //年齡 18
var p2 = new Person("章飛一絕",99);
p2.showName(); //姓名 章飛一絕
p2.showAge(); //年齡 99
像上面代碼這樣直接利用對(duì)象的動(dòng)態(tài)特性來設(shè)置原型對(duì)象,在原有原型對(duì)象的基礎(chǔ)上添加屬性和方法非常簡單,但是如果要添加的方法或?qū)傩员容^多,那么冗余代碼會(huì)比較多,這種情況推薦直接替換原有的原型對(duì)象。
//01 聲明構(gòu)造函數(shù)Person
function Person(name,age) {
this.name = name;
this.age = age || 18;
}
//02 給構(gòu)造函數(shù)原型對(duì)象添加方法
//設(shè)置原型對(duì)象的第二種方法:直接替換原先的原型對(duì)象
Person.prototype = {
constructor:Person,
showName:function () {
console.log("姓名 "+this.name);
},
showAge:function () {
console.log("年齡 "+this.age);
}
};
//04 使用構(gòu)造函數(shù)創(chuàng)建實(shí)例對(duì)象
var p = new Person("文頂頂");
p.showName(); //姓名 文頂頂
p.showAge(); //年齡 18
console.log(p.constructor); //Person函數(shù)
注意
如果是直接替換原型對(duì)象,那么需要修正構(gòu)造器屬性,讓constructor指向構(gòu)造函數(shù)。
說明
因?yàn)樘鎿Q的時(shí)候其實(shí)是用字面量的方式重新創(chuàng)建了新的對(duì)象,該對(duì)象作為Object構(gòu)造函數(shù)的原型對(duì)象,內(nèi)部沒有constructor屬性。這個(gè)時(shí)候如果要通過實(shí)例對(duì)象(比如p)的構(gòu)造器屬性判斷其類型,那么會(huì)先在p身上找,沒有則查找原型對(duì)象發(fā)現(xiàn)也沒有,最終得到的Object.prototype身上的構(gòu)造器屬性,結(jié)果為Object 。