作者:clearbug
原文地址:http://www.cnblogs.com/craftsman-gao/p/4824636.html
JavaScript中,除了五種原始類型(即數(shù)字站刑,字符串坊谁,布爾值枚赡,null馋记,undefined)之外的都是對象了,所以买羞,不把對象學(xué)明白怎么繼續(xù)往下學(xué)習(xí)呢非春?
一.概述
對象是一種復(fù)合值柱徙,它將很多值(原始值或其他對象)聚合在一起,可通過屬性名訪問這些值奇昙。而屬性名可以是包含空字符串在內(nèi)的任意字符串护侮。JavaScript對象也可以稱作一種數(shù)據(jù)結(jié)構(gòu),正如我們經(jīng)常聽說的“散列(hash)”储耐、“散列表(hashtable)”羊初、“字典(dictionary)”、“關(guān)聯(lián)數(shù)組(associative array)”什湘。
JavaScript中對象可以分為三類:
①內(nèi)置對象:例如數(shù)組长赞、函數(shù)、日期等闽撤;
②宿主對象:即JavaScript解釋器所嵌入的宿主環(huán)境(比如瀏覽器)定義的得哆,例如HTMLElement等;
③自定義對象:即程序員用代碼定義的哟旗;
對象的屬性可以分為兩類:
①自有屬性(own property):直接在對象中定義的屬性贩据;
②繼承屬性(inherited property):在對象的原型對象中定義的屬性(關(guān)于原型對象下面會詳談);
二.對象的創(chuàng)建
既然學(xué)習(xí)對象闸餐,又怎能不懂如何創(chuàng)建對象呢饱亮?面試前端崗位的同學(xué),可能都被問過這個(gè)基礎(chǔ)問題吧:
創(chuàng)建JavaScript對象的兩種方法是什么绎巨?(或者:說說創(chuàng)建JavaScript對象的方法近尚?)
這個(gè)問題我就被問過兩次〕∏冢“創(chuàng)建對象的兩種方法”這種說法網(wǎng)上有很多戈锻,但是據(jù)我所看書籍來說是有三種方法的!下面我們就來具體談?wù)勥@三種方法:
1.對象直接量
對象直接量由若干名/值對組成的映射表和媳,名/值對中間用冒號分隔格遭,名/值對之間用逗號分隔,整個(gè)映射表用花括號括起來留瞳。屬性名可以是JavaScript標(biāo)識符也可以是字符串直接量拒迅,也就是說下面兩種創(chuàng)建對象obj的寫法是完全一樣的:
var obj = {x: 1, y: 2};
var obj = {'x': 1, 'y':2};
2.通過new創(chuàng)建對象
new運(yùn)算符后跟隨一個(gè)函數(shù)調(diào)用,即構(gòu)造函數(shù)她倘,創(chuàng)建并初始化一個(gè)新對象璧微。例如:
1 var o = new Object(); //創(chuàng)建一個(gè)空對象,和{}一樣
2 var a = new Array(); //創(chuàng)建一個(gè)空數(shù)組硬梁,和[]一樣
3 var d = new Date(); //創(chuàng)建一個(gè)表示當(dāng)前時(shí)間的Date對象
關(guān)于構(gòu)造函數(shù)相關(guān)的內(nèi)容以后再說前硫。
3.Object.create()
ECMAScript5定義了一個(gè)名為Object.create()
的方法,它創(chuàng)建一個(gè)新對象荧止,其中第一個(gè)參數(shù)是這個(gè)對象的原型對象(好像還沒解釋原型對象...下面馬上就說)屹电,第二個(gè)可選參數(shù)用以對對象的屬性進(jìn)行進(jìn)一步的描述,第二個(gè)參數(shù)下面再說(因?yàn)檫@第三種方法是ECMAScript5中定義的跃巡,所以以前大家才經(jīng)常說創(chuàng)建對象的兩種方法的吧危号?個(gè)人覺得應(yīng)該是這個(gè)原因)。這個(gè)方法使用很簡單:
1 var o1 = Object.create({x: 1, y: 2}); //對象o1繼承了屬性x和y
2 var o2 = Object.create(null); //對象o2沒有原型
下面三種的完全一樣的:
1 var obj1 = {};
2 var obj2 = new Object();
3 var obj3 = Object.create(Object.prototype);
為了解釋為啥這三種方式是完全一樣的素邪,我們先來解釋下JavaScript中的原型對象(哎外莲,讓客官久等了!)兔朦,記得一位大神說過:
Javascript是一種基于對象(object-based)的語言偷线,你遇到的所有東西幾乎都是對象。但是烘绽,它又不是一種真正的面向?qū)ο缶幊蹋∣OP)語言淋昭,因?yàn)樗恼Z法中沒有class(類)。
面向?qū)ο蟮木幊陶Z言JavaScript安接,沒有類O韬觥!盏檐!那么歇式,它是怎么實(shí)現(xiàn)繼承的呢?沒錯胡野,就是通過原型對象材失。基本上每一個(gè)JavaScript對象(null除外)都和另一個(gè)對象相關(guān)聯(lián)硫豆,“另一個(gè)”對象就是所謂的原型對象(原型對象也可以簡稱為原型龙巨,并沒有想象的那么復(fù)雜笼呆,它也只是一個(gè)對象而已)。每一個(gè)對象都從原型對象繼承屬性旨别,并且一個(gè)對象的prototype
屬性的值(這個(gè)屬性在對象創(chuàng)建時(shí)默認(rèn)自動生成诗赌,并不需要顯示的自定義)就是這個(gè)對象的原型對象,即obj.prototype
就是對象obj的原型對象秸弛。
原型對象先說到這铭若,回到上面的問題,有了對原型對象的認(rèn)識递览,下面就是不需要過多解釋的JavaScript語言規(guī)定了:
①所有通過對象直接量創(chuàng)建的對象的原型對象就是Object.prototype
對象叼屠;
②通過關(guān)鍵字new和構(gòu)造函數(shù)創(chuàng)建的對象的原型對象就是構(gòu)造函數(shù)prototype
屬性的值,所以通過構(gòu)造函數(shù)Object創(chuàng)建的對象的原型就是Object.prototype
了绞铃;
現(xiàn)在也補(bǔ)充了第三種創(chuàng)建對象的方法Object.create()
第一個(gè)參數(shù)的含義镜雨。
三.屬性的查詢和設(shè)置
學(xué)會了如何創(chuàng)建對象還不夠啊,因?yàn)閷ο笾挥袚碛幸恍傩圆拍苷嬲鸬阶饔玫卧魇蓿∧敲蠢淅耄屠^續(xù)往下學(xué)習(xí)對象的屬性吧!
可以通過點(diǎn)(.)或方括號([])運(yùn)算符來獲取和設(shè)置屬性的值纯命。對于點(diǎn)(.)來說西剥,右側(cè)必須是一個(gè)以屬性名命名的標(biāo)識符(注意:JavaScript語言的標(biāo)識符有自己的合法規(guī)則,并不同于帶引號的字符串)亿汞;對于方括號([])來說瞭空,方括號內(nèi)必須是一個(gè)字符串表達(dá)式(字符串變量當(dāng)然也可以嘍,其他可以轉(zhuǎn)換成字符串的值比如數(shù)字什么的也是都可以滴)疗我,這個(gè)字符串就是屬性的名字咆畏。正如下面例子:
1 var obj = {x: 1, y: 2};
2 obj.x = 5;
3 obj['y'] = 6
概述中說過,JavaScript對象具有”自有屬性“吴裤,也有“繼承屬性”旧找。當(dāng)查詢對象obj的屬性x
時(shí),首先會查找對象obj自有屬性中是否有x
麦牺,如果沒有钮蛛,就會查找對象obj的原型對象obj.prototype
是否有屬性x
,如果沒有剖膳,就會進(jìn)而查找對象obj.prototype
的原型對象obj.prototype.prototype
是否有屬性x
魏颓,就這樣直到找到x或者查找到的原型對象是undefined
的對象為止≈ㄉ梗可以看到甸饱,一個(gè)對象上面繼承了很多原型對象,這些原型對象就構(gòu)成了一個(gè)”鏈“,這也就是我們平時(shí)所說的“原型鏈”叹话,這種繼承也就是JavaScript中“原型式繼承”(prototypal inheritance)偷遗。
對象o查詢某一屬性時(shí)正如上面所說會沿著原型鏈一步步查找,但是其設(shè)置某一屬性的值時(shí)渣刷,只會修改自有屬性(如果對象沒有這個(gè)屬性鹦肿,那就會添加這個(gè)屬性并賦值)矗烛,并不會修改原型鏈上其他對象的屬性辅柴。
四.存取器屬性getter和setter
上面我們所說的都是很普通的對象屬性,這種屬性稱做“數(shù)據(jù)屬性”(data property)瞭吃,數(shù)據(jù)屬性只有一個(gè)簡單的值碌嘀。然而在ECMAScript 5中,屬性值可以用一個(gè)或兩個(gè)方法替代歪架,這兩個(gè)方法就是getter
和setter
股冗,有getter
和setter
定義的屬性稱做“存取器屬性”(accessor property)。
當(dāng)程序查詢存取器屬性的值時(shí)和蚪,JavaScript調(diào)用getter方法(無參數(shù))止状。這個(gè)方法的返回值就是屬性存取表達(dá)式的值。當(dāng)程序設(shè)置一個(gè)存取器屬性的值時(shí)攒霹,JavaScript調(diào)用setter
方法怯疤,將賦值表達(dá)式右側(cè)的值當(dāng)做參數(shù)傳入setter。如果屬性同時(shí)具有getter
和setter
方法催束,那么它就是一個(gè)讀/寫屬性集峦;如果它只有getter
方法,那么它就是一個(gè)只讀屬性抠刺,給只讀屬性賦值不會報(bào)錯塔淤,但是并不能成功;如果它只有setter
方法速妖,那么它是一個(gè)只寫屬性高蜂,讀取只寫屬性總是返回undefined
『比荩看個(gè)實(shí)際的例子:
var p = {
x: 1.0,
y: 2.0,
get r(){ return Math.sqrt(this.x*this.x + this.y*this.y); };
set r(newvalue){
var oldvalue = Math.sqrt(this.x*this.x + this.y*this.y);
var ratio = newvalue/oldvalue;
this.x *= ratio;
this.y *= ratio;
},
get theta(){ return Math.atan2(this.y, this.x); },
print: function(){ console.log('x:'+this.x+', y:'+this.y); }
};
正如例子所寫备恤,存取器屬性定義一個(gè)或兩個(gè)和屬性同名的函數(shù),這個(gè)函數(shù)定義并沒有使用function
關(guān)鍵字杀赢,而是使用get
和set
烘跺,也沒有使用冒號將屬性名和函數(shù)體分隔開。對比一下脂崔,下面的print
屬性是一個(gè)函數(shù)方法裳瘪。
注意:這里的getter
和setter
里this
關(guān)鍵字的用法,JavaScript把這些函數(shù)當(dāng)做對象的方法來調(diào)用蔽挠,也就是說,在函數(shù)體內(nèi)的this指向這個(gè)對象铺敌。下面看下實(shí)例運(yùn)行結(jié)果:
正如控制臺的輸出,r屁擅、theta同x偿凭,y一樣只是一個(gè)值屬性,print是一個(gè)方法屬性派歌。ECMAScript 5增加的這種存取器弯囊,雖然比普通屬性更為復(fù)雜了,但是也使得操作對象屬性鍵值對更加嚴(yán)謹(jǐn)了胶果。
五.刪除屬性
程序猿擼碼一般都是實(shí)現(xiàn)增匾嘱、刪、改早抠、查功能霎烙,前面已經(jīng)說了增、改蕊连、查悬垃,下面就說說刪除吧!
delete運(yùn)算符可以刪除對象的屬性甘苍,它的操作數(shù)應(yīng)該是一個(gè)屬性訪問表達(dá)式尝蠕。但是,delete只是斷開屬性和宿主對象的聯(lián)系羊赵,而不會去操作屬性中的屬性:
1 var a = {p:{x:1}};
2 var b = a.p;
3 delete a.p;
執(zhí)行這段代碼后b.x
的值依然是1趟佃,由于已刪除屬性的引用依然存在,所以有時(shí)這種不嚴(yán)謹(jǐn)?shù)拇a會造成內(nèi)存泄露昧捷,所以在銷毀對象的時(shí)候闲昭,要遍歷屬性中的屬性,依次刪除靡挥。
delete表達(dá)式返回true的情況:
①刪除成功或沒有任何副作用(比如刪除不存在的屬性)時(shí)序矩;
②如果delete后不是一個(gè)屬性訪問表達(dá)式;
var obj = {x: 1,get r(){return 5;},set r(newvalue){this.x = newvalue;}};
delete obj.x; //刪除對象obj的屬性x跋破,返回true
delete obj.x; //刪除不存在的屬性簸淀,返回true
delete obj.r; //刪除對象obj的屬性r,返回true
delete obj.toString; //沒有任何副作用(toString是繼承來的毒返,并不能刪除)租幕,返回true
delete 1; //數(shù)字1不是屬性訪問表達(dá)式,返回true
delete表達(dá)式返回false的情況:
①刪除可配置性(可配置性是屬性的一種特性拧簸,下面會談到)為false的屬性時(shí)劲绪;
delete Object.prototype; //返回false,prototype屬性是不可配置的
//通過var聲明的變量或function聲明的函數(shù)是全局對象的不可配置屬性
var x = 1;
delete this.x; //返回false
function f() {}
delete this.f; //返回false
六.屬性的特性
上面已經(jīng)說到了屬性的可配置性特性,因?yàn)橄旅嬉f的檢測屬性和枚舉屬性還要用到屬性的特性這些概念贾富,所以現(xiàn)在就先具體說說屬性的特性吧歉眷!
除了包含名字和值之外,屬性還包含一些標(biāo)識它們可寫颤枪、可枚舉汗捡、可配置的三種特性。在ECMAScript 3中無法設(shè)置這些特性畏纲,所有通過ECMAScript 3的程序創(chuàng)建的屬性都是可寫的扇住、可枚舉的和可配置的,且無法對這些特性做修改霍骄。ECMAScript 5中提供了查詢和設(shè)置這些屬性特性的API台囱。這些API對于庫的開發(fā)者非常有用,因?yàn)椋?/p>
①可以通過這些API給原型對象添加方法读整,并將它們設(shè)置成不可枚舉的,這讓它們更像內(nèi)置方法;
②可以通過這些API給對象定義不能修改或刪除的屬性咱娶,借此“鎖定”這個(gè)對象米间;
在這里我們將存取器屬性的getter
和setter
方法看成是屬性的特性。按照這個(gè)邏輯膘侮,我們也可以把屬性的值同樣看做屬性的特性屈糊。因此,可以認(rèn)為屬性包含一個(gè)名字和4個(gè)特性琼了。數(shù)據(jù)屬性的4個(gè)特性分別是它的值(value)逻锐、可寫性(writable)、可枚舉性(enumerable)和可配置性(configurable)雕薪。存取器屬性不具有值特性和可寫性它們的可寫性是由setter方法是否存在與否決定的昧诱。因此存取器屬性的4個(gè)特性是讀取(get)所袁、寫入(set)盏档、可枚舉性和可配置性。
為了實(shí)現(xiàn)屬性特性的查詢和設(shè)置操作燥爷,ECMAScript 5中定義了一個(gè)名為“屬性描述符”(property descriptor)的對象蜈亩,這個(gè)對象代表那4個(gè)特性。描述符對象的屬性和它們所描述的屬性特性是同名的前翎。因此稚配,數(shù)據(jù)屬性的描述符對象的屬性有value
、writable
港华、enumerable
和configurable
道川。存取器屬性的描述符對象則用get
屬性和set
屬性代替value
和writable
。其中writable
、enumerable
和configurable
都是布爾值愤惰,當(dāng)然苇经,get
屬性和set
屬性是函數(shù)值。通過調(diào)用Object.getOwnPropertyDescriptor()
可以獲得某個(gè)對象特定屬性的屬性描述符:
從函數(shù)名字就可以看出宦言,Object.getOwnPropertyDescriptor()
只能得到自有屬性的描述符扇单,對于繼承屬性和不存在的屬性它都返回undefined
。要想獲得繼承屬性的特性奠旺,需要遍歷原型鏈(不會遍歷原型鏈蜘澜?不要急,下面會說到的)响疚。
要想設(shè)置屬性的特性鄙信,或者想讓新建屬性具有某種特性,則需要調(diào)用Object.definePeoperty()
忿晕,傳入需要修改的對象装诡、要創(chuàng)建或修改的屬性的名稱以及屬性描述符對象:
可以看到:
①傳入Object.defineProperty()
的屬性描述符對象不必包含所有4個(gè)特性;
②可寫性控制著對屬性值的修改践盼;
③可枚舉性控制著屬性是否可枚舉(枚舉屬性鸦采,下面會說的);
④可配置性控制著對其他特性(包括前面說過的屬性是否可以刪除)的修改咕幻;
如果要同時(shí)修改或創(chuàng)建多個(gè)屬性渔伯,則需要使用Object.defineProperties()
。第一個(gè)參數(shù)是要修改的對象肄程,第二個(gè)參數(shù)是一個(gè)映射表锣吼,它包含要新建或修改的屬性的名稱,以及它們的屬性描述符蓝厌,例如:
var p = Object.defineProperties({},{
x: {value: 1, writable: true, enumerable: true, configurable: true},
y: {value: 2, writable: true, enumerable: true, configurable: true},
r: {get: function(){return 88;}, set: function(newvalue){this.x =newvalue;},enumerable: true, configurable: true},
greet: {value: function(){console.log('hello,world');}, writable: true, enumerable: true, configurable: true}
});
相信你也已經(jīng)從實(shí)例中看出:Object.defineProperty()
和Object.defineProperties()
都返回修改后的對象玄叠。
前面我們說getter和setter存取器屬性時(shí)使用對象直接量語法給新對象定義存取器屬性,但并不能查詢屬性的getter
和setter
方法或給已有的對象添加新的存取器屬性褂始。在ECMAScript 5中诸典,就可以通過Object.getOwnPropertyDescriptor()
和Object.defineProperty()
來完成這些工作啦!但在ECMAScript 5之前崎苗,大多數(shù)瀏覽器(IE除外啦)已經(jīng)支持對象直接量語法中的get和set寫法了狐粱。所以這些瀏覽器還提供了非標(biāo)準(zhǔn)的老式API用來查詢和設(shè)置getter和setter。這些API有4個(gè)方法組成胆数,所有對象都擁有這些方法肌蜻。
__lookupGetter__()
和__lookupSetter__()
用以返回一個(gè)命名屬性的getter
和setter
方法.__defineGetter__()
和__defineSetter__()
用以定義getter
和setter
。這四個(gè)方法都是以兩條下劃線做前綴必尼,兩條下劃線做后綴蒋搜,以表明它們是非標(biāo)準(zhǔn)方法篡撵。下面是它們用法:
七.檢測屬性
JavaScript對象可以看做屬性的集合,那么我們有時(shí)就需要判斷某個(gè)屬性是否存在于某個(gè)對象中豆挽,這就是接下來要說的檢測屬性育谬。
檢測一個(gè)對象的屬性也有三種方法,下面就來詳細(xì)說說它們的作用及區(qū)別帮哈!
1.in運(yùn)算符
in運(yùn)算符左側(cè)是屬性名(字符串)膛檀,右側(cè)是對象。如果對象的自有屬性或繼承屬性中包含這個(gè)屬性則返回true娘侍,否則返回false咖刃。
為了試驗(yàn),我們先給對象Object.prototype
添加一個(gè)可枚舉屬性m
憾筏,一個(gè)不可枚舉屬性n
嚎杨;然后,給對象obj定義兩個(gè)可枚舉屬性x
,一個(gè)不可枚舉屬性y
氧腰,并且對象obj是通過對象直接量形式創(chuàng)建的枫浙,繼承了Object.prototype
。下面看實(shí)例:
從運(yùn)行結(jié)果可以看出:in運(yùn)算符左側(cè)是屬性名(字符串)容贝,右側(cè)是對象自脯。如果對象的自有屬性或繼承屬性(不論這些屬性是否可枚舉)中包含這個(gè)屬性則返回true,否則返回false斤富。
2.hasOwnProperty()
對象的hasOwnProperty()
方法用來檢測給定的名字是否是對象的自有屬性(不論這些屬性是否可枚舉),對于繼承屬性它將返回false锻狗。下面看實(shí)例:
3.propertyIsEnumerable()
propertyIsEnumerable()
是hasOwnProperty()
的增強(qiáng)版满力,只有檢測到是自有屬性且這個(gè)屬性可枚舉性為true時(shí)它才返回true。還是實(shí)例:
八.枚舉屬性
相對于檢測屬性轻纪,我們更常用的是枚舉屬性油额。枚舉屬性我們通常使用for/in循環(huán),它可以在循環(huán)體中遍歷對象中所有可枚舉的自有屬性和繼承屬性刻帚,把屬性名稱賦值給循環(huán)變量潦嘶。繼續(xù)上實(shí)例:
我原來認(rèn)為for/in循環(huán)跟in運(yùn)算符有莫大關(guān)系的,現(xiàn)在看來它們的規(guī)則并不相同俺缰凇掂僵!當(dāng)然,如果這里不想遍歷出繼承的屬性顷歌,那就在for/in循環(huán)中加一層hasOwnProperty()
判斷:
for(prop in obj){
if(obj.hasOwnProperty(prop)){
console.log(prop);
}
}
除了for/in循環(huán)之外锰蓬,ECMAScript 5還定義了兩個(gè)可以枚舉屬性名稱的函數(shù):
①Object.getOwnpropertyNames()
,它返回對象的所有自有屬性的名稱眯漩,不論是否可枚舉芹扭;
②Object.keys()
麻顶,它返回對象對象中可枚舉的自有屬性的名稱;
還是實(shí)例:
九.對象的三個(gè)特殊屬性
每個(gè)對象都有與之相關(guān)的原型(prototype)舱卡、類(class)和可擴(kuò)展性(extensible attribute)辅肾。這三個(gè)就是對象的特殊屬性(它們也只是對象的屬性而已,并沒有想象的復(fù)雜哦)轮锥。
1.原型屬性
正如前面所說矫钓,對象的原型屬性是用來繼承屬性的(有點(diǎn)繞...),這個(gè)屬性如此重要交胚,以至于我們經(jīng)常把“o的原型屬性”直接叫做“o的原型”份汗。原型屬性是在實(shí)例創(chuàng)建之初就設(shè)置好的(也就是說,這個(gè)屬性的值是JavaScript默認(rèn)自動設(shè)置的蝴簇,后面我們會說如何自己手動設(shè)置)杯活,前面也提到:
①通過對象直接量創(chuàng)建的對象使用Object.prototype
作為它們的原型;
②通過new+
構(gòu)造函數(shù)創(chuàng)建的對象使用構(gòu)造函數(shù)的prototype
屬性作為它們的原型熬词;
③通過Object.create()
創(chuàng)建的對象使用第一個(gè)參數(shù)(如果這個(gè)參數(shù)為null
旁钧,則對象原型屬性值為undefined
;如果這個(gè)參數(shù)為undefined
互拾,則會報(bào)錯:Uncaught TypeError: Object prototype may only be an Object or null: undefined)
作為它們的原型歪今;
那么,如何查詢一個(gè)對象的原型屬性呢颜矿?在ECMAScript 5中寄猩,將對象作為參數(shù)傳入Object.getPrototypeOf()
可以查詢它的原型,例如:
但是在ECMAScript 3中骑疆,沒有Object.getPrototypeOf()
函數(shù)田篇,但經(jīng)常使用表達(dá)式obj.constructor.prototype
來檢測一個(gè)對象的原型,因?yàn)槊總€(gè)對象都有一個(gè)constructor
屬性表示這個(gè)對象的構(gòu)造函數(shù):
①通過對象直接量創(chuàng)建的對象的constructor
屬性指向構(gòu)造函數(shù)Object()
箍铭;
②通過new+
構(gòu)造函數(shù)創(chuàng)建的對象的constructor
屬性指向構(gòu)造函數(shù)泊柬;
③通過Object.create()
創(chuàng)建的對象的constructor
屬性指向與其原型對象的constructor
屬性指向相同;
要檢測一個(gè)對象是否是另一個(gè)對象的原型(或處于原型鏈中)诈火,可以使用isPrototypeOf()
方法兽赁。例如:
還有一個(gè)非標(biāo)準(zhǔn)但眾多瀏覽器都已實(shí)現(xiàn)的對象的屬性__proto__
(同樣是兩個(gè)下劃線開始和結(jié)束,以表明其為非標(biāo)準(zhǔn))冷守,用以直接查詢/設(shè)置對象的原型刀崖。
2.類屬性
對象的類屬性(class attribute)是一個(gè)字符串,用以表示對象的類型信息教沾。ECMAScript 3 和ECMAScript 5 都未提供設(shè)置這個(gè)屬性的方法蒲跨,并只有一種間接的方法可以查詢它。默認(rèn)的toString()
方法(繼承自Object.prototype
)返回了這種格式的字符串:[object class]
授翻。因此或悲,要想獲得對象的類孙咪,可以調(diào)用對象的toString()
方法,然后提取已返回字符串的第8到倒數(shù)第二個(gè)位置之間的字符巡语。不過翎蹈,很多對象繼承的toString()
方法重寫了(比如:Array、Date等)男公,為了能調(diào)用正確的toString()
版本荤堪,必須間接地調(diào)用Function.call()
方法。下面代碼可以返回傳遞給它的任意對象的類:
function classof(obj){
if(o === null){
return 'Null';
}
if(o === undefined){
return 'Undefined';
}
return Object.prototype.toString.call(o).slice(8, -1);
}
classof()
函數(shù)可以傳入任何類型的參數(shù)枢赔。下面是使用實(shí)例:
總結(jié):從運(yùn)行結(jié)果可以看出通過三種方式創(chuàng)建的對象的類屬性都是'Object'
澄阳。
3.可擴(kuò)展性
對象的可擴(kuò)展性用以表示是否可以給對象添加新屬性。所有內(nèi)置對象和自定義對象都是顯示可擴(kuò)展的(除非將它們轉(zhuǎn)換為不可擴(kuò)展)踏拜,宿主對象的可擴(kuò)展性是由JavaScript引擎定義的碎赢。ECMAScript 5中定義了用來查詢和設(shè)置對象可擴(kuò)展性的函數(shù):
①(查詢)通過將對象傳入Object.isExtensible()
,來判斷該對象是否是可擴(kuò)展的速梗。
②(設(shè)置)如果想將對象轉(zhuǎn)換為不可擴(kuò)展肮塞,需要調(diào)用Object.preventExtensions()
,將待轉(zhuǎn)換的對象作為參數(shù)傳進(jìn)去姻锁。注意:
a.一旦將對象轉(zhuǎn)換為不可擴(kuò)展的枕赵,就無法再將其轉(zhuǎn)換回可擴(kuò)展的了;
b.preventExtensions()
只影響到對象本身的可擴(kuò)展性位隶,如果給一個(gè)不可擴(kuò)展的對象的原型添加屬性拷窜,這個(gè)不可擴(kuò)展的對象同樣會繼承這些新屬性;
進(jìn)一步涧黄,Object.seal()
和Object.preventExtensions()
類似装黑,除了能將對象設(shè)置為不可擴(kuò)展的,還可以將對象的所有自有屬性都設(shè)置為不可配置的弓熏。對于那些已經(jīng)封閉(sealed)起來的對象是不能解封的】匪可以使用Object.isSealed()
來檢測對象是否封閉挽鞠。更進(jìn)一步,Object.freeze()
將更嚴(yán)格地鎖定對象——“凍結(jié)”(frozen)狈孔。除了將對象設(shè)置為不可擴(kuò)展和將其屬性設(shè)置為不可配置之外信认,還可以將它自有的所有數(shù)據(jù)屬性設(shè)置為只讀(若對象的存取器屬性有setter
方法,存取器屬性將不受影響均抽,仍可通過給屬性賦值調(diào)用它們)嫁赏。使用Object.isFrozen()
來檢測對象是否總結(jié)。
總結(jié):Object.preventExtensions()
油挥、Object.seal()
和Object.freeze()
都返回傳入的對象潦蝇,也就是說款熬,可以通過嵌套的方式調(diào)用它們:
1 var obj = Object.seal(Object.create(Object.freeze({x:1}),{y:{value: 2, writable: true}));
這條語句中使用Object.create()
函數(shù)傳入了兩個(gè)參數(shù),即第一個(gè)參數(shù)是創(chuàng)建出的對象的原型對象攘乒,第二個(gè)參數(shù)是在創(chuàng)建對象是直接給其定義的屬性贤牛,并且附帶定義了屬性的特性。
十.對象的序列化
前面說完了對象的屬性以及對象屬性的特性则酝,東西還是蠻多的殉簸,不知道你是否已看暈。不過沽讹,下面就是比較輕松的話題了般卑!
對象序列化(serialization)是指將對象的狀態(tài)轉(zhuǎn)換為字符串,也可以將字符串還原為對象爽雄。ECMAScript 5提供了內(nèi)置函數(shù)JSON.stringify()
和JSON.parse()
用來序列化和還原對象蝠检。這些方法都使用JSON作為數(shù)據(jù)交換格式,JSON的全稱是“JavaScript Object Notation”——JavaScript對象表示法盲链,它的語法和JavaScript對象與數(shù)組直接量的語法非常相近:
其中蝇率,最后的jsonObj是obj的深拷貝(關(guān)于什么是深拷貝,什么是淺拷貝刽沾,可以參考:知乎本慕,第二個(gè)答案)。
JSON的語法是JavaScript的子集侧漓,它并不能表示JavaScript里的所有值锅尘。支持對象、數(shù)組布蔗、字符串藤违、無窮大數(shù)字、true纵揍、false和null顿乒,并且它們可以序列化和還原。注意:
①NaN泽谨、Infinity和-Infinity序列化的結(jié)果是null璧榄;
②JSON.stringify()
只能序列化對象可枚舉的自有屬性;
③日期對象序列化的結(jié)果是ISO格式的日期字符串(參照Date.toJSON()
函數(shù))吧雹,但JSON.parse()
依然保留它們的字符串形態(tài)骨杂,而不能將它們還原為原始日期對象;
④函數(shù)雄卷、RegExp搓蚪、Error對象和undefined值不能序列化和還原;
當(dāng)然丁鹉,JSON.stringify()
和JSON.parse()
都可以接受第二個(gè)可選參數(shù)妒潭,通過傳入需要序列化或還原的屬性列表來定制自定義的序列化或還原操作悴能,這個(gè)我們以后再詳談。