面向?qū)ο?/h1>

今天來淺談下面向?qū)ο螅嫦驅(qū)ο蟮恼Z言有一個(gè)標(biāo)志,即擁有類的概念顶岸,抽象實(shí)例對象的公共屬性與方法朱浴,基于類可以創(chuàng)建任意多個(gè)實(shí)例對象生兆,一般具有封裝、繼承、多態(tài)的特性!但JS中對象與純面向?qū)ο笳Z言中的對象是不同的肆资,ECMA標(biāo)準(zhǔn)定義JS中對象:無序?qū)傩缘募希鋵傩钥梢园局翟钪ァο蠡蛘吆瘮?shù)郑原。可以簡單理解為JS的對象是一組無序的值夜涕,其中的屬性或方法都有一個(gè)名字犯犁,根據(jù)這個(gè)名字可以訪問相映射的值(值可以是基本值/對象/方法)。

一女器、理解對象:

第一種:基于Object對象

varperson =newObject();

person.name ='My Name';

person.age = 18;

person.getName =function(){

returnthis.name;

}

第二種:對象字面量方式(比較清楚的查找對象包含的屬性及方法)

varperson = {

name :'My name',

age : 18,

getName :function(){

returnthis.name;

}

}

JS的對象可以使用‘.’操作符動態(tài)的擴(kuò)展其屬性酸役,可以使用’delete’操作符或?qū)傩灾翟O(shè)置為’undefined’來刪除屬性。如下:

person.newAtt=’newAttr’;//添加屬性

alert(person.newAtt);//new Attr

delete person.age;

alert(person.age);//undefined(刪除屬性后值為undefined);

二晓避、對象屬性類型

ECMA-262第5版定義了JS對象屬性中特征(用于JS引擎簇捍,外部無法直接訪問)。ECMAScript中有兩種屬性:數(shù)據(jù)屬性和訪問器屬性

1俏拱、數(shù)據(jù)屬性:

數(shù)據(jù)屬性指包含一個(gè)數(shù)據(jù)值的位置暑塑,可在該位置讀取或?qū)懭胫担搶傩杂?個(gè)供述其行為的特性:

[[configurable]]:表示能否使用delete操作符刪除從而重新定義锅必,或能否修改為訪問器屬性事格。默認(rèn)為true;

[[Enumberable]]:表示是否可通過for-in循環(huán)返回屬性。默認(rèn)true;

[[Writable]]:表示是否可修改屬性的值搞隐。默認(rèn)true;

[[Value]]:包含該屬性的數(shù)據(jù)值驹愚。讀取/寫入都是該值。默認(rèn)為undefined;如上面實(shí)例對象person中定義了name屬性劣纲,其值為’My name’,對該值的修改都反正在這個(gè)位置

要修改對象屬性的默認(rèn)特征(默認(rèn)都為true)逢捺,可調(diào)用Object.defineProperty()方法,它接收三個(gè)參數(shù):屬性所在對象癞季,屬性名和一個(gè)描述符對象(必須是:configurable劫瞳、enumberable、writable和value绷柒,可設(shè)置一個(gè)或多個(gè)值)志于。

如下:(瀏覽器支持:IE9+、Firefox 4+废睦、Chrome伺绽、Safari5+)

varperson = {};

Object.defineProperty(person,'name', {

configurable:false,

writable:false,

value:'Jack'

});

alert(person.name);//Jack

delete person.name;

person.name ='lily';

alert(person.name);//Jack

可以看出,delete及重置person.name的值都沒有生效嗜湃,這就是因?yàn)檎{(diào)用defineProperty函數(shù)修改了對象屬性的特征奈应;值得注意的是一旦將configurable設(shè)置為false,則無法再使用defineProperty將其修改為true(執(zhí)行會報(bào)錯(cuò):can't redefine non-configurable property);

2购披、訪問器屬性:

它主要包括一對getter和setter函數(shù)钥组,在讀取訪問器屬性時(shí),會調(diào)用getter返回有效值今瀑;寫入訪問器屬性時(shí)程梦,調(diào)用setter,寫入新值橘荠;該屬性有以下4個(gè)特征:

[[Configurable]]:是否可通過delete操作符刪除重新定義屬性屿附;

[[Numberable]]:是否可通過for-in循環(huán)查找該屬性;

[[Get]]:讀取屬性時(shí)調(diào)用哥童,默認(rèn):undefined;

[[Set]]:寫入屬性時(shí)調(diào)用挺份,默認(rèn):undefined;

訪問器屬性不能直接定義,必須使用defineProperty()來定義贮懈,如下:

var person = {

_age: 18

};

Object.defineProperty(person,'isAdult', {

get:function() {

if(this._age >= 18) {

returntrue;

}else{

returnfalse;

}

}

});

alert(person.isAdult?'成年':'未成年');//成年

從上面可知匀泊,定義訪問器屬性時(shí)getter與setter函數(shù)不是必須的,并且优训,在定義getter與setter時(shí)不能指定屬性的configurable及writable特性;

此外各聘,ECMA-262(5)還提供了一個(gè)Object.defineProperties()方法揣非,可以用來一次性定義多個(gè)屬性的特性:

var person = {};

Object.defineProperties(person,{

_age:{

value:19

},

isAdult:{

get:function() {

if(this._age >= 18) {

returntrue;

}else{

returnfalse;

}

}

}

});

alert(person.isAdult?'成年':'未成年');//成年

上述代碼使用Object.defineProperties()方法同時(shí)定義了_age及isAudlt兩個(gè)屬性的特性

此外,使用Object.getOwnPropertyDescriptor()方法可以取得給定屬性的特性:

vardescriptor = Object.getOwnPropertyDescriptor(person,'_age');

alert(descriptor.value);//19

對于數(shù)據(jù)屬性躲因,可以取得:configurable,enumberable,writable和value早敬;

對于訪問器屬性,可以取得:configurable,enumberable,get和set

三大脉、創(chuàng)建對象

使用Object構(gòu)造函數(shù)或?qū)ο笞置媪慷伎梢詣?chuàng)建對象搞监,但缺點(diǎn)是創(chuàng)建多個(gè)對象時(shí),會產(chǎn)生大量的重復(fù)代碼镰矿,因此下面介紹可解決這個(gè)問題的創(chuàng)建對象的方法

1琐驴、工廠模式

functioncreatePerson(name, age, job) {

varo =newObject();

o.name = name;

o.age = age;

o.job = job;

o.getName =function() {

returnthis.name;

}

returno;//使用return返回生成的對象實(shí)例

}

varperson = createPerson('Jack', 19,'SoftWare Engineer');

創(chuàng)建對象交給一個(gè)工廠方法來實(shí)現(xiàn),可以傳遞參數(shù)秤标,但主要缺點(diǎn)是無法識別對象類型棍矛,因?yàn)閯?chuàng)建對象都是使用Object的原生構(gòu)造函數(shù)來完成的。

2抛杨、構(gòu)造函數(shù)模式

functionPerson(name,age,job){

this.name = name;

this.age = age;

this.job = job;

this.getName =function() {

returnthis.name;

}

}

varperson1 =newPerson('Jack', 19,'SoftWare Engineer');

varperson2 =newPerson('Liye', 23,'Mechanical Engineer');

使用自定義的構(gòu)造函數(shù)(與普通函數(shù)一樣够委,只是用它來創(chuàng)建對象),定義對象類型(如:Person)的屬性和方法怖现。它與工廠方法區(qū)別在于:

沒有顯式地創(chuàng)建對象

直接將屬性和方法賦值給this對象茁帽;

沒有return語句;

此外屈嗤,要?jiǎng)?chuàng)建Person的實(shí)例潘拨,必須使用new關(guān)鍵字,以Person函數(shù)為構(gòu)造函數(shù)饶号,傳遞參數(shù)完成對象創(chuàng)建铁追;實(shí)際創(chuàng)建經(jīng)過以下4個(gè)過程:

創(chuàng)建一個(gè)對象

將函數(shù)的作用域賦給新對象(因此this指向這個(gè)新對象,如:person1)

執(zhí)行構(gòu)造函數(shù)的代碼

返回該對象

上述由Person構(gòu)造函數(shù)生成的兩個(gè)對象person1與person2都是Person的實(shí)例茫船,因此可以使用instanceof判斷琅束,并且因?yàn)樗袑ο蠖祭^承Object,因此person1 instanceof Object也返回真:

alert(person1 instanceof Person);//true;

alert(person2 instanceof Person);//true;

alert(person1 instanceof Object);//true;

alert(person1.constructor === person2.constructor);//ture;

雖然構(gòu)造函數(shù)方式比較不錯(cuò)算谈,但也存在缺點(diǎn)涩禀,那就是在創(chuàng)建對象時(shí),特別針對對象的屬性指向函數(shù)時(shí)然眼,會重復(fù)的創(chuàng)建函數(shù)實(shí)例艾船,以上述代碼為基礎(chǔ),可以改寫為:

functionPerson(name,age,job){

this.name = name;

this.age = age;

this.job = job;

this.getName =newFunction () {//改寫后效果與原代碼相同,不過是為了方便理解

returnthis.name;

}

}

上述代碼屿岂,創(chuàng)建多個(gè)實(shí)例時(shí)践宴,會重復(fù)調(diào)用new Function();創(chuàng)建多個(gè)函數(shù)實(shí)例,這些函數(shù)實(shí)例還不是一個(gè)作用域中爷怀,當(dāng)然這一般不會有錯(cuò)阻肩,但這會造成內(nèi)存浪費(fèi)。當(dāng)然霉撵,可以在函數(shù)中定義一個(gè)getName = getName的引用,而getName函數(shù)在Person外定義洪囤,這樣可以解決重復(fù)創(chuàng)建函數(shù)實(shí)例問題徒坡,但在效果上并沒有起到封裝的效果,如下所示:

functionPerson(name,age,job){

this.name = name;

this.age = age;

this.job = job;

this.getName = getName;

}

functiongetName() {//到處是代碼瘤缩,看著亂@辍!

returnthis.name;

}

3剥啤、原型模式

JS每個(gè)函數(shù)都有一個(gè)prototype(原型)屬性锦溪,這個(gè)屬性是一個(gè)指針,指向一個(gè)對象府怯,它是所有通過new操作符使用函數(shù)創(chuàng)建的實(shí)例的原型對象刻诊。原型對象最大特點(diǎn)是,所有對象實(shí)例共享它所包含的屬性和方法牺丙,也就是說则涯,所有在原型對象中創(chuàng)建的屬性或方法都直接被所有對象實(shí)例共享。

functionPerson(){

}

Person.prototype.name ='Jack';//使用原型來添加屬性

Person.prototype.age = 29;

Person.prototype.getName =function(){

returnthis.name;

}

varperson1 =newPerson();

alert(person1.getName());//Jack

varperson2 =newPerson();

alert(person1.getName === person2.getName);//true;共享一個(gè)原型對象的方法

原型是指向原型對象的冲簿,這個(gè)原型對象與構(gòu)造函數(shù)沒有太大關(guān)系粟判,唯一的關(guān)系是函數(shù)的prototype是指向這個(gè)原型對象!而基于構(gòu)造函數(shù)創(chuàng)建的對象實(shí)例也包含一個(gè)內(nèi)部指針為:[[prototype]]指向原型對象峦剔。

實(shí)例屬性或方法的訪問過程是一次搜索過程:

首先從對象實(shí)例本身開始档礁,如果找到屬性就直接返回該屬性值;

如果實(shí)例本身不存在要查找屬性吝沫,就繼續(xù)搜索指針指向的原型對象呻澜,在其中查找給定名字的屬性,如果有就返回惨险;

基于以上分析易迹,原型模式創(chuàng)建的對象實(shí)例,其屬性是共享原型對象的平道;但也可以自己實(shí)例中再進(jìn)行定義睹欲,在查找時(shí),就不從原型對象獲取,而是根據(jù)搜索原則窘疮,得到本實(shí)例的返回袋哼;簡單來說,就是實(shí)例中屬性會屏蔽原型對象中的屬性闸衫;

原型與in操作符

一句話:無論原型中屬性涛贯,還是對象實(shí)例的屬性,都可以使用in操作符訪問到蔚出;要想判斷是否是實(shí)例本身的屬性可以使用object.hasOwnProperty(‘a(chǎn)ttr’)來判斷弟翘;

原生對象中原型

原生對象中原型與普通對象的原型一樣,可以添加/修改屬性或方法骄酗,如以下代碼為所有字符串對象添加去左右空白原型方法:

String.prototype.trim =function(){

returnthis.replace(/^\s+/,'').replace(/\s+$/,'');

}

varstr ='? word space? ';

alert('!'+str.trim()+'!');//!word space!

原型模式的缺點(diǎn)稀余,它省略了為構(gòu)造函數(shù)傳遞初始化參數(shù),這在一定程序帶來不便趋翻;另外睛琳,最主要是當(dāng)對象的屬性是引用類型時(shí),它的值是不變的踏烙,總是引用同一個(gè)外部對象师骗,所有實(shí)例對該對象的操作都會其它實(shí)例:

functionPerson() {

}

Person.prototype.name ='Jack';

Person.prototype.lessons = ['Math','Physics'];

varperson1 =newPerson();

person1.lessons.push('Biology');

varperson2 =newPerson();

alert(person2.lessons);//Math,Physics,Biology,person1修改影響了person2

4讨惩、組合構(gòu)造函數(shù)及原型模式

目前最為常用的定義類型方式辟癌,是組合構(gòu)造函數(shù)模式與原型模式。構(gòu)造函數(shù)模式用于定義實(shí)例的屬性荐捻,而原型模式用于定義方法和共享的屬性愿待。結(jié)果,每個(gè)實(shí)例都會有自己的一份實(shí)例屬性的副本靴患,但同時(shí)又共享著對方方法的引用仍侥,最大限度的節(jié)約內(nèi)存。此外鸳君,組合模式還支持向構(gòu)造函數(shù)傳遞參數(shù)农渊,可謂是集兩家之所長。

functionPerson(name, age, job) {

this.name = name;

this.age = age;

this.job = job;

this.lessons = ['Math','Physics'];

}

Person.prototype = {

constructor: Person,//原型字面量方式會將對象的constructor變?yōu)镺bject或颊,此外強(qiáng)制指回Person

getName:function() {

returnthis.name;

}

}

varperson1 =newPerson('Jack', 19,'SoftWare Engneer');

person1.lessons.push('Biology');

varperson2 =newPerson('Lily', 39,'Mechanical Engneer');

alert(person1.lessons);//Math,Physics,Biology

alert(person2.lessons);//Math,Physics

alert(person1.getName === person2.getName);//true,//共享原型中定義方法

在所接觸的JS庫中砸紊,jQuery類型的封裝就是使用組合模式來實(shí)例的!4烟簟醉顽!

5、動態(tài)原型模式

組合模式中實(shí)例屬性與共享方法(由原型定義)是分離的平挑,這與純面向?qū)ο笳Z言不太一致游添;動態(tài)原型模式將所有構(gòu)造信息都封裝在構(gòu)造函數(shù)中系草,又保持了組合的優(yōu)點(diǎn)。其原理就是通過判斷構(gòu)造函數(shù)的原型中是否已經(jīng)定義了共享的方法或?qū)傩运衾裕绻麤]有則定義找都,否則不再執(zhí)行定義過程。該方式只原型上方法或?qū)傩灾欢x一次廊酣,且將所有構(gòu)造過程都封裝在構(gòu)造函數(shù)中能耻,對原型所做的修改能立即體現(xiàn)所有實(shí)例中:

functionPerson(name, age, job) {

this.name = name;

this.age = age;

this.job = job;

this.lessons = ['Math','Physics'];

}

if (typeofthis.getName !='function') {//通過判斷實(shí)例封裝

Person.prototype = {

constructor: Person,//原型字面量方式會將對象的constructor變?yōu)镺bject,此外強(qiáng)制指回Person

getName:function () {

returnthis.name;

}

}

}

varperson1 =newPerson('Jack', 19,'SoftWare Engneer');

person1.lessons.push('Biology');

varperson2 =newPerson('Lily', 39,'Mechanical Engneer');

alert(person1.lessons);//Math,Physics,Biology

alert(person2.lessons);//Math,Physics

alert(person1.getName === person2.getName);//true,//共享原型中定義方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者

  • 序言:七十年代末亡驰,一起剝皮案震驚了整個(gè)濱河市晓猛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌凡辱,老刑警劉巖戒职,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異煞茫,居然都是意外死亡帕涌,警方通過查閱死者的電腦和手機(jī)摄凡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門续徽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人亲澡,你說我怎么就攤上這事钦扭。” “怎么了床绪?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵客情,是天一觀的道長。 經(jīng)常有香客問我癞己,道長膀斋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任痹雅,我火速辦了婚禮仰担,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绩社。我一直安慰自己摔蓝,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布愉耙。 她就那樣靜靜地躺著贮尉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪朴沿。 梳的紋絲不亂的頭發(fā)上猜谚,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天败砂,我揣著相機(jī)與錄音,去河邊找鬼龄毡。 笑死吠卷,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的沦零。 我是一名探鬼主播祭隔,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼路操!你這毒婦竟也來了疾渴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤屯仗,失蹤者是張志新(化名)和其女友劉穎搞坝,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體魁袜,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡桩撮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了峰弹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片店量。...
    茶點(diǎn)故事閱讀 40,488評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鞠呈,靈堂內(nèi)的尸體忽然破棺而出融师,到底是詐尸還是另有隱情,我是刑警寧澤蚁吝,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布旱爆,位于F島的核電站,受9級特大地震影響窘茁,放射性物質(zhì)發(fā)生泄漏怀伦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一山林、第九天 我趴在偏房一處隱蔽的房頂上張望房待。 院中可真熱鬧,春花似錦捌朴、人聲如沸吴攒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽洼怔。三九已至,卻和暖如春左驾,著一層夾襖步出監(jiān)牢的瞬間镣隶,已是汗流浹背极谊。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留安岂,地道東北人轻猖。 一個(gè)月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像域那,于是被迫代替她去往敵國和親咙边。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容