JavaScript原型對(duì)象、構(gòu)造函數(shù)和實(shí)例對(duì)象功能與用法詳解
本文實(shí)例講述了JavaScript原型對(duì)象、構(gòu)造函數(shù)和實(shí)例對(duì)象功能與用法。分享給大家供大家參考,具體如下:
大家都知道,javascript中其實(shí)并沒(méi)有類(lèi)的概念。但是,用構(gòu)造函數(shù)跟原型對(duì)象卻可以模擬類(lèi)的實(shí)現(xiàn)。在這里,就先很不嚴(yán)謹(jǐn)?shù)氖褂妙?lèi)這個(gè)詞,以方便說(shuō)明。
下面整理了一些關(guān)于javascript的構(gòu)造函數(shù)、原型對(duì)象以及實(shí)例對(duì)象的筆記,有錯(cuò)誤的地方,望指正。
先用一張圖簡(jiǎn)單的概括下這幾者之間的關(guān)系,再細(xì)化:
構(gòu)造函數(shù)和實(shí)例對(duì)象
構(gòu)造函數(shù)是類(lèi)的外在表現(xiàn),構(gòu)造函數(shù)的名字通常用作類(lèi)名。
其實(shí)構(gòu)造函數(shù)也就是一個(gè)函數(shù),只不過(guò)它于普通的函數(shù)又有點(diǎn)不同:
- 沒(méi)有顯示的創(chuàng)建對(duì)象;
- 直接將屬性和方法賦給
this
; - 沒(méi)有
return
語(yǔ)句;
構(gòu)造函數(shù)是用來(lái)構(gòu)造新對(duì)象的。之前的筆記中有提到過(guò),可以是用new關(guān)鍵詞來(lái)調(diào)用構(gòu)造函數(shù),以創(chuàng)建特定類(lèi)型的新對(duì)象。如,創(chuàng)建一個(gè)Object類(lèi)型的對(duì)象實(shí)例:
var o=new Object();
為了區(qū)別構(gòu)造函數(shù)和普通函數(shù),通常規(guī)定構(gòu)造函數(shù)的命名首字母大寫(xiě),而普通函數(shù)的命名首字母小寫(xiě)。當(dāng)然,這不是必須的,卻是一個(gè)很好的習(xí)慣。
通過(guò)用構(gòu)造函數(shù)創(chuàng)建并初始化的屬性是實(shí)例屬性。所謂的實(shí)例屬性就是指,通過(guò)該構(gòu)造函數(shù)創(chuàng)建的每個(gè)對(duì)象,都將擁有一份實(shí)例屬性的單獨(dú)拷貝。這些屬性都是通過(guò)實(shí)例來(lái)訪問(wèn)的,值根據(jù)每個(gè)實(shí)例所定義的為準(zhǔn),若實(shí)例中沒(méi)有定義,則為構(gòu)造函數(shù)初始化時(shí)的默認(rèn)值。來(lái)看一個(gè)例子:
function Person(name,age){ this.name=name; this.age=age; this.friends=["Tom","Boo"]; } var p1=new Person("Lily",20); var p2=new Person("Sam",30); alert(p1.name); //Lily alert(p2.name); //Sam p1.friends.push("Susan"); alert(p1.friends); //Tom,Boo,Susan alert(p2.friends); //Tom,Boo
上面的例子定義了一個(gè)Person構(gòu)造函數(shù),并初始化了name、age和friends三個(gè)屬性。接著創(chuàng)建了兩個(gè)實(shí)例對(duì)象,分別為p1和p2。觀察這個(gè)例子,每個(gè)屬性都是為各自所擁有的,并不會(huì)相互影響。這就是因?yàn)槊總€(gè)實(shí)例對(duì)象都擁有一份屬性的副本。
每個(gè)實(shí)例對(duì)象都有一個(gè)屬性指向它的構(gòu)造函數(shù),這屬性就是constructor:
function Person(name,age){ this.name=name; this.age=age; } var p1=new Person("Lily",20); var p2=new Person("Sam",30); alert(p1.constructor==Person); //true alert(p2.constructor==Person); //true
構(gòu)造函數(shù)有一個(gè)prototype屬性,指向原型對(duì)象。
原型對(duì)象和實(shí)例對(duì)象
在javascript中,每個(gè)對(duì)象都有一個(gè)與之相關(guān)聯(lián)的對(duì)象,那就是它的原型對(duì)象。類(lèi)的所有實(shí)例對(duì)象都從它的原型對(duì)象上繼承屬性。
原型對(duì)象是類(lèi)的唯一標(biāo)識(shí):當(dāng)且僅當(dāng)兩個(gè)對(duì)象繼承自同一個(gè)原型對(duì)象時(shí),它們才是屬于同一個(gè)類(lèi)的實(shí)例。
前面有提到,構(gòu)造函數(shù)擁有一個(gè)prototype屬性,指向原型。換句話來(lái)說(shuō),一個(gè)對(duì)象的原型就是它的構(gòu)造函數(shù)的prototype屬性的值。當(dāng)一個(gè)函數(shù)被定義的時(shí)候,它會(huì)自動(dòng)創(chuàng)建和初始化prototype值,它是一個(gè)對(duì)象,這時(shí)這個(gè)對(duì)象只有一個(gè)屬性,那就是constructor,它指回和原型相關(guān)聯(lián)的那個(gè)構(gòu)造函數(shù)??磦€(gè)例子:
function Person(name,age){ this.name=name; this.age=age; } alert(Person.prototype); //[object Object] alert(Person.prototype.constructor==Person); //true
也可以通過(guò)原型來(lái)創(chuàng)建屬性和方法。通過(guò)原型創(chuàng)建的屬性和方法是被所有實(shí)例所共享的。即,在一個(gè)實(shí)例中修改了該屬性或方法的值,那么所有其他實(shí)例的屬性或方法值都會(huì)受到影響:
function Person(name,age){ this.name=name; this.age=age; } Person.prototype.friends=["Tom","Sam"]; var p1=new Person("Lily",24); var p2=new Person("Susan",20); alert(p1.friends); //Tom,Sam alert(p2.friends); //Tom,Sam p1.friends.push("Bill"); alert(p1.friends); //Tom,Sam,Bill alert(p2.friends); //Tom,Sam,Bill
由上面的例子可以看出,用原型定義的屬性是被所有實(shí)例共享的。為p1添加了一個(gè)朋友,導(dǎo)致p2也添加了這個(gè)朋友。
其實(shí),很多情況下,這種現(xiàn)象并不是我們想看到的。那么什么時(shí)候應(yīng)該用構(gòu)造函數(shù)初始化屬性和方法,哪些時(shí)候又該由原型對(duì)象來(lái)定義呢?
通常建議在構(gòu)造函數(shù)內(nèi)定義一般成員,即它的值在每個(gè)實(shí)例中都將不同,尤其是對(duì)象或數(shù)組形式的值;而在原型對(duì)象中則定義一些所有實(shí)例所共享的屬性,即在所有實(shí)例中,它的值可以是相同的屬性。
當(dāng)用構(gòu)造函數(shù)創(chuàng)建一個(gè)實(shí)例時(shí),實(shí)例的內(nèi)部也包含了一個(gè)指針,指向構(gòu)造函數(shù)的原型對(duì)象。一些瀏覽器中,支持一個(gè)屬性__proto__
來(lái)表示這個(gè)內(nèi)部指針:
function Person(name,age){ this.name=name; this.age=age; } Person.prototype.sayName=function(){ alert(this.name); } var p1=new Person("Lily",24); alert(p1.__proto__.sayName); //function (){alert(this.name);} alert(p1.__proto__.constructor==Person); //true
在ECMAscript5中新增了一個(gè)方法,Object.getPrototypeOf()
,可以返回前面提到的實(shí)例對(duì)象內(nèi)部的指向其原型的指針的值:
function Person(name,age){ this.name=name; this.age=age; } var p1=new Person("Lily",24); alert(Object.getPrototypeOf(p1)==Person.prototype); //true
isPrototypeOf()
方法也可用于確定實(shí)例對(duì)象和其原型之間的這種關(guān)系:
function Person(name,age){ this.name=name; this.age=age; } var p1=new Person("Lily",24); alert(Person.prototype.isPrototypeOf(p1)); //true
原型語(yǔ)法
從前面介紹原型對(duì)象于實(shí)例對(duì)象及構(gòu)造函數(shù)的關(guān)系中,我們已經(jīng)知道,給原型對(duì)象添加屬性和方法只要像這樣定義即可:Person.prototype=name
。
那么是否每定義一個(gè)Person的屬性,就要敲一遍Person.prototype
呢?答案是否定的,我們也可以像用對(duì)象字面量創(chuàng)建對(duì)象那樣來(lái)創(chuàng)建原型對(duì)象:
function Person(){ } Person.prototype={ name:"Tom", age:29 } var p1=new Person(); alert(p1.name); //Tom alert(p1.age); //29
有一點(diǎn)要注意,這個(gè)方法相當(dāng)于重寫(xiě)了整個(gè)原型對(duì)象,因此切斷了它與構(gòu)造函數(shù)的關(guān)系,此時(shí)Person.prototype.constructor
不再指向Person:
function Person(){ } Person.prototype={ name:"Tom", age:29 } var p1=new Person(); alert(Person.prototype.constructor==Person); //false alert(Person.prototype.constructor==Object); //true
因此,如果想要讓它重新指向Person,可以顯示的進(jìn)行賦值:
function Person(){ } Person.prototype={ constructor:Person, name:"Tom", age:29 } var p1=new Person(); alert(Person.prototype.constructor==Person); //true alert(Person.prototype.constructor==Object); //false
總結(jié)
最后,我們拿一個(gè)例子,再來(lái)理理構(gòu)造函數(shù)、原型對(duì)象以及實(shí)例對(duì)象之間的關(guān)系:
function Person(name,age){ this.name=name; this.age=age; } Person.prototype.sayName=function(){ alert(this.name); } var p1=new Person("Tom",20); alert(Person.prototype); //object alert(Person.prototype.constructor==Person); //true alert(p1.constructor==Person); //true alert(p1.__proto__==Person.prototype); //true alert(p1.__proto__.__proto__==Object.prototype); //true alert(p1.__proto__.__proto__.constructor==Object); //true alert(Person.constructor==Function); //true alert(Object.prototype.constructor==Object);
上圖說(shuō)明了這個(gè)例子中原型、構(gòu)造函數(shù)和實(shí)例屬性的關(guān)系。
更多關(guān)于JavaScript相關(guān)內(nèi)容感興趣的讀者可查看本站專(zhuān)題:《javascript面向?qū)ο笕腴T(mén)教程》、《JavaScript常用函數(shù)技巧匯總》、《JavaScript錯(cuò)誤與調(diào)試技巧總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》、《JavaScript遍歷算法與技巧總結(jié)》及《JavaScript數(shù)學(xué)運(yùn)算用法總結(jié)》
希望本文所述對(duì)大家JavaScript程序設(shè)計(jì)有所幫助。
相關(guān)文章
JS生態(tài)系統(tǒng)加速模塊解析賦能性能優(yōu)化探索
這篇文章主要為大家介紹了JS生態(tài)系統(tǒng)加速模塊解析賦能性能優(yōu)化探索,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01js頁(yè)面加載后執(zhí)行的幾種方式小結(jié)
在實(shí)際應(yīng)用中往往需要在頁(yè)面加載完畢之后再去執(zhí)行相關(guān)的js代碼,之所以這么操作是有道理的,如果是操作dom元素,如果相關(guān)元素沒(méi)有加載完成,而去執(zhí)行js代碼,可能會(huì)導(dǎo)致錯(cuò)誤2020-01-01深入理解JavaScript系列(14) 作用域鏈介紹(Scope Chain)
在第12章關(guān)于變量對(duì)象的描述中,我們已經(jīng)知道一個(gè)執(zhí)行上下文 的數(shù)據(jù)(變量、函數(shù)聲明和函數(shù)的形參)作為屬性存儲(chǔ)在變量對(duì)象中2012-04-04用javascript做一個(gè)webgame連連看大家看下
2008-01-01微信小程序分享卡片花樣玩法之私密消息和動(dòng)態(tài)消息
用戶可以發(fā)送小程序卡片給微信好友或者群,點(diǎn)擊小程序卡片可以直接進(jìn)入小程序,這篇文章主要給大家介紹了關(guān)于微信小程序分享卡片花樣玩法之私密消息和動(dòng)態(tài)消息的相關(guān)資料,需要的朋友可以參考下2023-11-11JS面向?qū)ο蠡A(chǔ)講解(工廠模式、構(gòu)造函數(shù)模式、原型模式、混合模式、動(dòng)態(tài)原型模式)
這篇文章主要介紹了面向?qū)ο驤S基礎(chǔ)講解,工廠模式、構(gòu)造函數(shù)模式、原型模式、混合模式、動(dòng)態(tài)原型模式,需要的朋友可以參考下2014-08-08