第六章:對象
2017.02.24
對象
對象是JavaScript的基本數(shù)據(jù)類型。對象是一種復(fù)合值。對象可以看做是屬性的無序集合,每個屬性都是一個名/值對。屬性名是字符串,可以把對象看成是從字符串到值得映射。
這種基本數(shù)據(jù)結(jié)構(gòu)還有多種叫法:散列(hash)、散列表(hashtable)、字典(dictionary)、關(guān)聯(lián)數(shù)組、(associative array)。
對象不僅僅是字符串到值得映射,除了可以保持自由的屬性,JavaScript對象可以從一個稱為原型的對象繼承屬性。對象的方法通常是集成的屬性。這種“原型式繼承”(prototypal inheritance)是JavaScript的核心特征。
JavaScript對象是動態(tài)的,可以新增屬性也可以刪除屬性。但他們常用來模擬靜態(tài)對象以及靜態(tài)類型語言中的結(jié)構(gòu)體,有時也用來做字符串的集合(忽略名/值隊(duì)中的值)。
除了字符串、數(shù)字、true、false、null、undefined外,JavaScript中的值都是對象。
對象是可變的,我們通過引用而非值來操作對象。如果變量x是指向一個對象的引用,那么var y=x;y也是指向同一個對象的引用,而非副本。通過變量y修改這個對象亦會對變量x造成影響。
對象最常用的方法是創(chuàng)建create、設(shè)置set、查找query、刪除delete、檢測test、枚舉enumerate其屬性。
屬性包括名字和值。屬性名可以是包含字符串在內(nèi)的任意字符串,對象中不能存在兩個同名的屬性(非嚴(yán)格模式下不會報(bào)錯,但是會覆蓋)。屬性還有“屬性特性”(property attribute):
- 可寫,表明是否可以設(shè)置該屬性的值。
- 可枚舉,表明是否可以通過for/in循環(huán)返回該屬性。
- 可配置,表明是否可以刪除或修改該屬性。
ES5之前,通過代碼給對象創(chuàng)建的所有屬性都是可寫的、可枚舉的??膳渲玫?。
除了包含屬性外,每個對象還擁有三個相關(guān)的對象特性(object attribute):
- 對象的原型(prototype)指向另一個對象,本對象的屬性繼承自它的原型對象。
- 對象的類(class)是一個標(biāo)識對象類型的字符串。
- 對象的擴(kuò)展標(biāo)記(extensible flag)指明了是否可以向該對象添加新屬性(ES5)。
【三類JavaScript對象】
- 內(nèi)置對象(native object):由ES規(guī)范定義的對象或類。例如數(shù)組、函數(shù)、日期、正則表達(dá)式。
- 宿主對象(host object):由JavaScript解釋器所嵌入的宿主環(huán)境(比如Web瀏覽器)定義的??蛻舳薐avaScript中表示網(wǎng)頁結(jié)構(gòu)的HTMLElement對象均是宿主對象。既然宿主環(huán)境定義的方法可以當(dāng)成普通的JavaScript函數(shù)對象,那么宿主對象也可以當(dāng)成內(nèi)置對象。
- 自定義對象(user-defined object):由運(yùn)行中的JavaScript代碼創(chuàng)建的對象。
【兩類屬性】
- 自有屬性(own property):直接在對象中定義的屬性。
- 繼承屬性(inherited property):在對象的原型對象中定義的屬性。
創(chuàng)建對象
對象直接量、關(guān)鍵字new、Object.create()(ES5中)
對象直接量
創(chuàng)建對象最簡單的方法是使用對象直接量。
對象直接量是由若干名/值對組成的映射表,名/值對中間用冒號分隔,名/值對之間用逗號分隔,整個映射表用花括號括起來。
屬性名可以是JavaScript標(biāo)識符也可以是字符串直接量(包括空字符串)。屬性的值可以是任意類型的JavaScript表達(dá)式,表達(dá)式的值可以是原始值也可以是對象值,就是這個屬性的值。
var empty={};
var point={x:0,y:0};
var book={
"main title":"JavaScript", //屬性名里有空格,必須用字符串表示
"sub-title":"The Definitive Guide", //屬性名里有連字符,必須用字符串表示
"for":"all audiences", //for是保留字,需用引號,(ES5以及ES3的一些實(shí)現(xiàn)可以不用引號)
author:{
firstname:"David",
surname:"Flanagan"
}
}
ES5中,對象直接量的最后一個屬性后的逗號將忽略,ES3中大部分可以忽略,但是IE報(bào)錯。
通過new創(chuàng)建對象
new運(yùn)算符創(chuàng)建并初始化一個新對象。關(guān)鍵字new后跟隨一個函數(shù)調(diào)用(構(gòu)造函數(shù) constructor)。構(gòu)造函數(shù)用以初始化一個新創(chuàng)建的對象。JavaScript語言核心中的原始類型都包含內(nèi)置構(gòu)造函數(shù)。
var o=new Object(); //空對象,和{}一樣
var a=new Array(); //空數(shù)組,和[]一樣
var d=new Date(); //當(dāng)前時間對象
var r=new RegExp("js"); //創(chuàng)建進(jìn)行模式匹配的RegExp對象
原型
每一個JavaScript對象(null除外)都和另一個對象相關(guān)聯(lián)。另一個對象就是原型,每一個對象都從原型繼承屬性。
所有通過對象直接量創(chuàng)建的對象都具有同一個原型對象,并可以通過JavaScript代碼Object.prototype獲得對原型對象的引用。
通過關(guān)鍵字new和構(gòu)造函數(shù)創(chuàng)建的對象的原型就是構(gòu)造函數(shù)的prototype屬性的值。因此,同使用{}創(chuàng)建對象一樣,通過new Object()創(chuàng)建的對象也繼承自O(shè)bject.prototype。同樣,通過new Array()創(chuàng)建的對象的原型就是Array.prototype,通過new Date()創(chuàng)建的對象的原型就是Date.prototype。
> Object.prototype
{}
> Array.prototype
[]
> Date.prototype
Date {}
> RegExp.prototype
RegExp {}
> Function.prototype
[Function]
> Function.prototype.toString()
'function () {}'
> Number.prototype
[Number: 0]
> String.prototype
[String: '']
> Boolean.prototype
[Boolean: false]
Object.prototype沒有原型對象。它不繼承任何屬性,其他對象都是普通對象,普通對象都具有原型。所有的內(nèi)置構(gòu)造函數(shù)(以及大部分自定義的構(gòu)造函數(shù))都具有一個繼承自O(shè)bject.prototype的原型。例如:Date.prototype的屬性繼承自O(shè)bject.prototype,因此由new Date()創(chuàng)建的Date對象的屬性同事繼承自Date.prototype和Object.prototype。這一系列鏈接的原型對象就是所謂的"原型鏈"(prototype chain)。
Object.create()
ES5定義了名為Object.create()方法,他創(chuàng)建一個新對象,其中第一個參數(shù)是這個對象的原型。第二個參數(shù)可選,用以對屬性進(jìn)行進(jìn)一步描述。
是靜態(tài)函數(shù),而不是提供給某個對象調(diào)用的方法。調(diào)用它的方法很簡單,只需傳入所需的原型對象即可。
var o=Object.create({x:1,y:1}); //o繼承了屬性x和y
通傳入?yún)?shù)null來創(chuàng)建一個沒有原型的新對象,通過此方式創(chuàng)建的對象不會繼承任何東西,甚至不包括基礎(chǔ)方法(比如toString)。也就是說,它不能和"+"運(yùn)算符一起正常工作。
var o=Object.create(null); //不繼承任何屬性和方法
創(chuàng)建一個普通的空對象,需要傳入Object.prototype。
var o=Object.create(Object.prototype); //同 {},new Object()一樣
可以通過任意原型創(chuàng)建新對象(即可以使任意對象可繼承)。
【通過原型繼承創(chuàng)建一個新對象】
// 函數(shù)返回繼承自原型對象p的屬性的新對象,防止庫函數(shù)無意間修改那些不受控制的對象。
function inherit(p){
if (p==null) throw TypeError(); //p是一個對象,但不能是null
if (Object.create) return Object.create(p);//如果存在Object.create()
var t=typeof p;
if (t!=="object" &&t!=="function") throw TypeError();
function f(){}; //定義一個空構(gòu)造函數(shù)
f.prototype=p; //將其原型屬性設(shè)置為p
return new f(); //使用f()創(chuàng)建p的繼承對象
}
屬性的查詢和設(shè)置
var author=book.author;
var name=author.surname;
var title=book["main title"];
book.edition=6;
book["main title"]="ECMAScript";
ES3中點(diǎn)運(yùn)算符后的標(biāo)識符不能是保留字。必須以方括號的形式訪問。比如,o["for"]。ES5可以再點(diǎn)運(yùn)算符后使用保留字。
//node 6.9.5
> var a={for:1}
undefined
> a
{ for: 1 }
> a.for
1
使用方括號時,方括號內(nèi)的表達(dá)式必須返回字符串或返回一個可以轉(zhuǎn)換為字符串的值。例如數(shù)組中的arr[0]。
作為關(guān)聯(lián)數(shù)組的對象
關(guān)聯(lián)數(shù)組:元素通過字符串索引而不是數(shù)字索引。如object["property"]
JavaScript對象都是關(guān)聯(lián)數(shù)組。
繼承
查找對象o屬性x,如果o中不存在x,將會在o的原型對象中查詢屬性x。如果原型中也沒有x,但這個原想對象也有原型,那么繼續(xù)向上執(zhí)行查詢,知道找到x或者查找到一個原型為null的對象為止。(應(yīng)該是直到找到x或者找到Object對象或者查找到一個原型是null的對象為止)。
給對象o屬性x賦值,如果o已經(jīng)有熟悉x,那么這個操作改變這個已有屬性x的值。如果o中不存在x,那么復(fù)制操作給o天劍新屬性x。如果之前o繼承了屬性x,繼承的屬性將會被同名的新屬性覆蓋掉。
對象的原型屬性構(gòu)成了一個"鏈",通過"鏈"可以實(shí)現(xiàn)屬性的繼承。
屬性訪問錯誤
查找不存在的屬性并不會報(bào)錯會返回undefined。
book.subtitle;//undefined
如果對象不存在,師徒查詢不存在的對象的屬性會報(bào)錯。null和undefined沒有屬性,因此會報(bào)錯。
book.subtitle.length;//類型錯誤
避免出錯的方法
var len=book&&book.subtitle&&book.subtitle.length;
ES5任何失敗的屬性設(shè)置操作都不會拋出類型錯誤異常。下屬場景下給對象o設(shè)置屬性p會失敗:
- o屬性p只讀:不能給只讀屬性重新賦值(defineProperty()方法下有一個個例外,可以對可配置的只讀屬性重新賦值)
- o的屬性p是繼承屬性,且只讀:不能通過同名自有屬性覆蓋只讀的繼承屬性。
- o中不存在自有屬性p:o沒有使用setter方法繼承屬性p,并且o的可擴(kuò)展性為false。如果o中不存在p,而且沒有setter方法,則p會添加到o中。但是如果o不可擴(kuò)展,那么o不能定義新屬性。
刪除屬性
delete運(yùn)算符可以刪除對象的屬性。操作數(shù)應(yīng)當(dāng)是一個屬性訪問表達(dá)式。
delete book.author;
delete book["main title"];
delete只是斷開屬性和宿主對象的聯(lián)系,而不去操作屬性中的屬性。
a={p:{x:1}};
b=a.p;
delete a.p; // 此時b為{x:1},a為{}
當(dāng)delete刪除成功或沒有任何作用時,翻譯true。如果delete后不是一個屬性訪問表達(dá)式,delete同樣返回true。
o={x:1};
delete o.x;//true
delete o.x; //什么也沒做,x已經(jīng)不存在了,返回true
delete o.toString; //什么也沒做,toString是繼承來的。返回true。
delete 1; //無意義,返回true
delete不能刪除可配置型為false的屬性(盡管可以刪除不可擴(kuò)展對象的可配置屬性)。某些內(nèi)置對象的屬性是不可配置的,比如通過變量聲明和函數(shù)聲明創(chuàng)建的全局對象的屬性。嚴(yán)格模式下(ES5),刪除不可配置屬性會報(bào)類型錯誤,非嚴(yán)格模式下(以及ES3)返回false。
delete Object.prototype; //不能刪除,屬性不可配置
var x=1;
delete this.x; //不能刪除全局變量
function f(){};
delete this.f; //不能刪除全局函數(shù)
非嚴(yán)格模式下刪除全局對象的可配置屬性式,可以省略對全局對象的引用。
this.x=1; //創(chuàng)建一個可配置的全局屬性(沒有用var)
delete x; //刪除成功
檢測屬性
JavaScript對象可以看做屬性的集合。檢測屬性集合中成員的所屬關(guān)系,判斷某個屬性是否存在于某個對象中,使用in、hasOwnProperty()和protertyIsEnumerable()方法來完成。
in
in運(yùn)算符左側(cè)是屬性名(字符串),右側(cè)是對象。如果對象的自有屬性或繼承屬性中包含這個屬性則返回true。
var o={x:1};
"x" in o; //true
"y" in o; //false
"toString" in o; //true :o繼承toString屬性
除了使用in,可以使用!==判斷一個屬性是否是undefined。
var o={x:1};
o.x !== undefined; //true,o中有屬性x
o.y !== undefined; //false,o中沒有shuxingy
o.toString !== undefinede; //true :o繼承toString屬性
in運(yùn)算符可以區(qū)分不存在的屬性和存在但值為undefined的屬性。
var o={x:undefined};
o.x!==undefined;//false,屬性存在但值為undefined
"x" in o;//true,屬性存在
delete o.x;
"x" in o;//false,屬性不再存在
if(o.x!=null) o.x*=2;//如果存在且不為null或undefined,o.x乘以2
if(o.x) o.x*=2;//如果o含有屬性且屬性不能轉(zhuǎn)換為false,o.x乘以2(undefined,null,false,"",0,NaN)
hasOwnProperty()
用來檢測給定的名字是否是對象的自有屬性。對于繼承屬性它將返回false。
var o={x:1};
o.hasOwnProperty("x"); //true
o.hasOwnProperty("y"); //false
o.hasOwnProperty("toString"); //false,繼承屬性返回false
propertyIsEnumerable
檢測自有屬性且這個屬性的可枚舉性為true時返回true。某些內(nèi)置屬性是不可枚舉的,通常由JavaScript代碼創(chuàng)建的屬性都是可枚舉的,除非在ES5中使用特殊方法改變屬性的可枚舉性。
var o=inherit({y:2});
o.x=1;
o.propertyIsEnumerable("x"); //true,o有一個可枚舉的自有屬性x
o.propertyIsEnumerable("y"); //false,y是繼承來的
Object.prototype.propertyIsEnumerable("toString"); //false,不可枚舉
枚舉屬性
for/in 遍歷所有可枚舉的屬性(包括自有的和繼承的),把屬性名賦值給循環(huán)變量。
for(p in o){
if (!o.hasOwnProperty(p)) continue;//跳過繼承的屬性
}
for(p in o){
if (typeof o[p] === "function") continue;//跳過函數(shù)
}
Object,keys() ,返回一個數(shù)組,數(shù)組由對象中可枚舉的自有屬性的名稱組成。(ES5)
Object.getOwnPropertyNames(),返回對象的所有自有屬性的名稱。(ES5)
屬性getter和setter
對象屬性由名字、值和一組特性構(gòu)成。ES5中,屬性值可以用一個或兩個方法替代,這兩個方法就是getter和setter。由getter和setter定義的屬性稱作"存儲器屬性"(accessor property),它不同于”數(shù)據(jù)屬性“(data property),數(shù)據(jù)屬性只有一個簡單地值。
當(dāng)程序查詢存儲器屬性的值時,JavaScript調(diào)用getter方法(無參數(shù))。返回值就是屬性存取表達(dá)式的值。當(dāng)程序設(shè)置一個存取器屬性的值時,JavaScript調(diào)用setter方法,將賦值表達(dá)式右側(cè)的值當(dāng)做參數(shù)傳入setter。
存取器屬性不具有可寫性。如果屬性同時具有g(shù)etter,setter方法,那么它是一個讀寫屬性。如果只有g(shù)etter或setter方法,則它是一個只讀/寫屬性。讀取只寫屬性總是返回undefined。
定義存取器屬性最簡單的方法是使用對象直接量語法的一種擴(kuò)展寫法:
var o={
//普通的數(shù)據(jù)屬性
data_prop:value,
//存取器屬性都是成對定義的函數(shù)
get accessor_prop(){/*函數(shù)體*/},
set accessor_prop(value){/*函數(shù)體*/}
};
【表示2D笛卡爾點(diǎn)坐標(biāo)的對象】
> var p={x:1.0,
... y:1.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);}
... };
undefined
> p.r
1.4142135623730951
> p.r=2
2
> p.r
1.9999999999999998
> p
{ x: 1.414213562373095,
y: 1.414213562373095,
r: [Getter/Setter],
theta: [Getter] }
> p.theta
0.7853981633974483
> p.theta=1
1
> p.theta
0.7853981633974483
與數(shù)據(jù)屬性一樣,存取器屬性是可以繼承的。
屬性的特性
數(shù)據(jù)屬性的四個特性:值、可寫性,可枚舉性、可配置性。
存取器屬性不具有值特性和可寫性,他們的可寫性由setter方法存在與否決定。存取器的四個特性:讀取、寫入、可枚舉性、可配置型。
屬性描述符
屬性描述符(property descriptor)對象代表四個特性。描述符對象的屬性和他們所描述的屬性特性是同名的。
數(shù)據(jù)屬性的描述符對象的屬性有value、writable、enumerable、configurable。
存儲器屬性的描述符對象的屬性有g(shù)et、set、enumerable、configurable。
其中writable、enumerable、configurable都是布爾值,get屬性和set屬性是函數(shù)值。
Object.getOwnPropertyDescriptor()
通過Object.getOwnPropertyDescriptor()獲取某個對象特定屬性的屬性描述符,對于不存在的屬性和繼承屬性返回undefined:
Object.getOwnPropertyDescriptor({x:1},"x");//返回{ value: 1, writable: true, enumerable: true, configurable: true }
Object.getOwnPropertyDescriptor(p,"r");//返回{ get: [Function: get r],set: [Function: set r], enumerable: true, configurable: true }
Object.getOwnPropertyDescriptor(p,"theta");//返回 { get: [Function: get theta], set: undefined, enumerable: true, configurable: true }
Object.getOwnPropertyDescriptor({},"x");//undefined,不存在的
Object.getOwnPropertyDescriptor({},"toString");//undefined,繼承的
獲取繼承屬性的特性,需要遍歷原型鏈。
Object.defineProperty()
設(shè)置屬性的特性,或者想讓新建屬性具有某種特性,則需要調(diào)用 Object.defineProperty(),傳入要修改的對象、要創(chuàng)建或修改的屬性的額名稱以及屬性描述符對象:
var o={};
//添加不可枚舉的數(shù)據(jù)屬性x賦值為1
Object.defineProperty(o,"x",{value:1,writable:true,enumerable:false,configurable:true});
o.x; //1 ,屬性存在
Object.keys(o);//[],不可枚舉
//修改為只讀
Object.defineProperty(o,"x",{writable:false});
o.x=2;//操作失敗但不報(bào)錯,嚴(yán)格模式下報(bào)錯
o.x; //1
//屬性依然是可以配置的,因此可以通過這種方式對它進(jìn)行修改:
Object.defineProperty(o,"x",{value:2});
o.x;// 2
//將x從數(shù)據(jù)屬性修改為存取器屬性
Object.defineProperty(o,"x",{get:function(){return 0;}});
o.x;//0
Object.getOwnPropertyDescriptor(o,"x");//{ get: [Function: get],set: undefined, enumerable: false, configurable: true }
傳入的屬性描述符對象不必包含所有4個特性。對于新創(chuàng)建的屬性來說,默認(rèn)的特征值是false或undefined。對于修改的已有屬性來說,默認(rèn)的特征值沒有做任何修改。注意:這個方法要么修改已有的自有屬性要么新建自有屬性,但不能修改繼承屬性。
Object.defineProperties()
如果要同時修改或創(chuàng)建多個屬性,則需要使用Object.defineProperties()。第一個參數(shù)是要修改的對象,第二個參數(shù)是一個映射表,包含要新建或修改的屬性的名稱以及他們的屬性描述符。
var p=Object.defineProperties({},{
x:{value:1,writable:true,enumerable:true,configurable:true},
y:{value:1,writable:true,enumerable:true,configurable:true},
r:{
get:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},
enumerable:true,
configurable:true
}
});
p; //{ x: 1, y: 1, r: [Getter] }
對于不允許創(chuàng)建或修改的屬性來說,使用 Object.defineProperty()和Object.defineProperties()對其操作就會拋出類型錯誤異常。
- 如果對象是不可擴(kuò)展的,則可以編輯已有的自有屬性,但不能給它添加新屬性。
- 如果屬性是不可配置的,則不能修改它的可配置型和可枚舉性。
- 如果存取器屬性是不可配置的,則不能修改其getter和setter方法,也不能將它轉(zhuǎn)換為數(shù)據(jù)屬性。
- 如果數(shù)據(jù)屬性是不可配置的,則不能將它轉(zhuǎn)換為存取器屬性。
- 如果數(shù)據(jù)屬性是不可配置的,則不能將它的可寫性從false修改為true,但可以從true修改為false。
- 如果數(shù)據(jù)屬性是不可配置的且不可寫的,則不能修改它的值??膳渲玫豢蓪憣傩缘闹凳强梢孕薷牡模▽?shí)際上是先將它標(biāo)記為可寫的,然后修改它的值,最后轉(zhuǎn)換為不可寫的)。
【復(fù)制屬性的特性】
/*
* 給Object.property添加不可枚舉的extend()方法
* 這個方法繼承自調(diào)用它的對象,將作為參數(shù)傳入的對象的屬性一一復(fù)制
* 除了值之外,也復(fù)制屬性的所有屬性,除非在目標(biāo)對象中存在同名的屬性
* 參數(shù)對象的所有自身對象(包括不可枚舉的屬性也會一一復(fù)制)
*/
Object.defineProperty(Object.prototype,"extend",{
writable:true,
enumerable:false,
configurable:true,
value:function(o){
//得到所有的自有屬性,包括不可枚舉屬性
var names=Object.getOwnPropertyNames(o);
//遍歷
for(var i=0;i<names.length;i++){
//如果屬性已經(jīng)存在,跳過
if(names[i] in this) continus;
//獲得o中的屬性的描述符
var desc=Object.getOwnPropertyDescriptor(o,names[i]);
//用它給this創(chuàng)建一個屬性
Object.defineProperty(this,names[i],desc);
}
}
});
> Object.getOwnPropertyDescriptor(Object.prototype,"extend")
{ value: [Function: value],
writable: true,
enumerable: false,
configurable: true }
對象的三個屬性
原型(prototype)、類(class)、可擴(kuò)展性(extensible attribute)
原型屬性
對象的原型屬性是用來繼承屬性的。我們經(jīng)常把"o的原型屬性"直接叫做"o的原型"。
Object.getPrototypeOf()
Object.getPrototypeOf() (ES5)查詢原型。
var p={x:1};
var o=Object.create(p);
Object.getPrototypeOf(p);//{}
Object.getPrototypeOf(o);//{ x: 1 }
isPrototypeOf()
isPrototypeOf() 檢測一個對象是否是另一個對象的原型(或處于原型鏈中)。p.isPrototypeOf(o)檢測p是否是o的原型,o是否繼承自p
var p={x:1};
var o=Object.create(p);
p.isPrototypeOf(o); //true,o繼承自p
Object.prototype.isPrototypeOf(o); //true,p繼承自O(shè)bject.prototype
類屬性
對象的類屬性是一個字符串,用以表示對象的類型信息。ES3和ES5都未提供設(shè)置這個屬性的方法。并只有一種間接的方法可以查詢它。默認(rèn)的toString()方法返回了如下格式的字符串:[object class]
// classof() 返回傳遞給它的任意對象的類
function classof(o){
if(o===null) return "Null";
if(o===undefined) return "Undefined";
return Object.prototype.toString.call(o).slice(8,-1);
}
classof(null) //"Null"
classof(1) //"Number"
classof("") //"String"
classof(true) //"Boolean"
classof({}) //"Object"
classof([]) //"Array"
classof(/./) //"RegExp"
classof(new Date()) //"Date"
classof(window) //"Window",客戶端宿主對象。nodejs下會報(bào)錯 ReferenceError: window is not defined
function f() {};
classof(f); //"function"
classof(new f()); //"Object"
可擴(kuò)展性
對象的可擴(kuò)展性用以表示是否可以給對象添加新屬性。所有的內(nèi)置對象和自定義對象都是顯示可擴(kuò)展的,宿主對象的可擴(kuò)展性是由JavaScript引擎定義的。ES5中,所有內(nèi)置對象和自定義對象都是可擴(kuò)展的,除非將他們轉(zhuǎn)換為不可擴(kuò)展的。
ES5定義了查詢和設(shè)置對象可擴(kuò)展性的函數(shù)。
Object.esExtensible()
將對象傳入Object.isExtensible(),判斷對象是否是可擴(kuò)展的。
var a={};
Object.isExtensible(a); //true
Object.preventExtensions()
如果想將對象轉(zhuǎn)換為不可擴(kuò)展的,需要調(diào)用Object.preventExtensions()。注意,一旦將對象轉(zhuǎn)換為不可擴(kuò)展的,就無法再將其轉(zhuǎn)換為可擴(kuò)展的了。此方法只影響到對象本神的可擴(kuò)展性,如果給一個不可擴(kuò)展的對象的原型添加屬性,這個不可擴(kuò)展的對象同樣會繼承這些新屬性。
var a={x:1};
var b=Object.create(a)
Object.preventExtensions(b)
Object.isExtensible(b)//false
a.y=1;
b.y;//1
b.m=1
b.m;//undefined
Object.seal()
Object.seal()將對象設(shè)置為不可擴(kuò)展的,還可以講對象的所有自有屬性都設(shè)置為不可配置的。對于封閉(sealed)起來的對象是不能解封的,可以通過Object.isSealed()來檢測對象是否封閉。
//接上:
Object.isSealed(a); // false
Object.isSealed(b); // true
Object.freeze()
Object.freeze()將更嚴(yán)格地鎖定對象——"凍結(jié)"(frozen)。將對象設(shè)置為不可擴(kuò)展的,將其屬性設(shè)置為不可配置的,將它自由的所有數(shù)據(jù)屬性設(shè)置為只讀的。(如果對象存取器屬性具有setter方法,存取器屬性將不受影響)。使用Object,isFrozen()來檢測對象是否凍結(jié)。
//接上
Object.isFrozen(a); //false
Object.isFrozen(b); //true
序列化對象
序列化對象(serialization)是將對象的狀態(tài)轉(zhuǎn)換為字符串,也可以將字符串還原為對象。
ES5 提供內(nèi)置函數(shù)JSON.stringify()和JSON.parse()用來序列化和還原JavaScript對象。這些方法都使用JSON(JavaScript Object Notation,JavaScript對象表示法)作為數(shù)據(jù)交換格式。
o={x:1,y:{z:[false,null,""]}};
s=JSON.stringify(o);//'{"x":1,"y":{"z":[false,null,""]}}'
p=JSON.parse(s);//{ x: 1, y: { z: [ false, null, '' ] } }
JSON的語法是JavaScript語法的子集,它并不能表示JavaScript里的所有值。
JSON官網(wǎng),在此不對json做深究。
【簡單測試如下,基于node 6.9.5】
> JSON.stringify(undefined)
ndefined //無返回值
> JSON.stringify(null)
'null'
> JSON.stringify(NaN)
'null'
> JSON.stringify(0)
'0'
> JSON.stringify('')
'""'
> JSON.stringify(true)
'true'
> JSON.stringify(false)
'false'
> JSON.stringify({})
'{}'
> JSON.stringify([1,2,3])
'[1,2,3]'
> JSON.stringify(function(){})
undefined //無返回值
> JSON.stringify(new Date())
'"2017-02-24T08:40:02.442Z"'
> JSON.stringify(/./)
'{}'
> JSON.parse('null')
null
> JSON.parse('0')
0
> JSON.parse('""')
''
> JSON.parse('true')
true
> JSON.parse('false')
false
> JSON.parse('{}')
{}
> JSON.parse('[1,2,3]')
[ 1, 2, 3 ]
> JSON.parse('"2017-02-24T08:40:02.442Z"')
'2017-02-24T08:40:02.442Z'
對象方法
toString() valueOf() 可以參考JavaScript 中的 toString() 與 valueOf()
toLocaleString()
返回一個表示這個對象的本地化字符串。
Object中默認(rèn)的toLocaleString() 方法并不做任何本地化自身的操作,它僅調(diào)用toString()方法并返回對應(yīng)值。
Date和Number類對其做了定制,可以用它對數(shù)字、日期和時間做本地化的轉(zhuǎn)換。
> (new Date()).toString()
'Fri Feb 24 2017 17:07:46 GMT+0800 (CST)'
> (new Date()).toLocaleString()
'2017-02-24 17:07:50'
Array類的toLocaleString對每個數(shù)組元素調(diào)用toLocaleString()方法轉(zhuǎn)換為字符串。