對象是一種復(fù)合值:它將很多值(原始值或其他對象)聚合在一起窄做,可通過名字訪問這些值。
對象也可看做是屬性的無序集合河泳,每個屬性都是一個名/值對伶授。
除了包含屬性之外断序,每個對象還擁有3個特性(object attribute):
- 對象的原型(prototype):指向另外一個原型對象,本對象的屬性繼承自它的原型對象糜烹。
- 對象的類(class):一個標(biāo)識對象類型的字符串违诗。
- 對象的擴展標(biāo)記(extensible flag):指明是否可以向該對象添加新屬性。
JavaScript中包含了3類對象和2類屬性:
- 內(nèi)置對象(native object):由ECMAScript規(guī)范定義的對象或類疮蹦。如诸迟,Array, function, Date, RegExp等。
- 宿主對象(host object): 由JavaScript解釋器所嵌入的宿主環(huán)境(如Wbd瀏覽器)定義的愕乎。如阵苇,表示網(wǎng)頁結(jié)構(gòu)的HTMLElement對象。
- 自定義對象(user-defined object):由運行中的JavaScript代碼創(chuàng)建的對象感论。
- 繼承屬性(inherited property):在對象的原型中定義的屬性绅项。
- 自有屬性(own property):直接在對象中定義的屬性。
對象的創(chuàng)建
直接量創(chuàng)建對象
對象直接量是同若干名/值對組成的映射表比肄,名/值對中間用冒號分隔快耿,名/值對之間用逗號分隔,整個映射表用花括號括起來芳绩。
var empty = {}; // 沒有任何屬性的對象
var point = { x:0, y:0 }; // 包含2個屬性的對象
var point2 = { x:point.x, y:point.y+1 } // 使用對象的值來賦值屬性的值
var book = {
"main title":"JavaScript", // 屬性名字里有空格掀亥,必須用引號
"sub-title":"The Definitive Guide", // 屬性名字里有連字符,必須用引號
"for":"all audiences", // "for"是保留字示括,必須使用引號
author:{ // 對象屬性
firstname:"David",
surname:"Flanagan"
}
}
對象直接量是一個表達(dá)式,這個表達(dá)式的每次運算都創(chuàng)建并初始化一個新的對象痢畜。
通過new創(chuàng)建對象
new運算符創(chuàng)建并初始化一個新對象垛膝,關(guān)鍵字new后面跟隨一個構(gòu)造函數(shù)(constructor)調(diào)用。
var o = new Object(); // 創(chuàng)建一個空對象丁稀,和{}一樣(只包含Object.prototype)
var a = new Array(); // 創(chuàng)建一個空數(shù)組吼拥,和[]一樣
var d = new Date(); // 創(chuàng)建一個表示當(dāng)前時間的Date對象
var r = new RegExp("js"); // 創(chuàng)建一個可以模式匹配的RegExp對象
通過Object.creat()創(chuàng)建對象
Object.creat()是一個靜態(tài)函數(shù),其中第一個參數(shù)是待創(chuàng)建對象的原型线衫,第二個參數(shù)可選凿可,用以對對象的屬性進(jìn)行進(jìn)一步描述。
var o1 = Object.create(Object.property); // 創(chuàng)建一個空對象,同{}枯跑、new Object()一樣
var o2 = Object.create({x:1, y:2}); // 創(chuàng)建一個新對象惨驶,并繼承了屬性x和y
var o3 = Object.creat(null); // 創(chuàng)建一個對象,但不繼承任何屬性及方法
可以通過任意原型創(chuàng)建新對象敛助,如:
// inherit()返回一個繼承自原型對象p屬性的新對象
function inherit(p) {
// p是一個對象粗卜,但不能是null
if(p == null) throw TypeError();
// 如果Object.create()存在,則直接使用它來創(chuàng)建對象
if(Object.create)
return Object.create(p)
var t = typeof p;
if(t !== "object" && t !== "function") throw TypeError();
// 通過prototype屬性創(chuàng)建對象
function f() {}
f.prototype = p;
return new f();
}
inherit()并不能代替Object.create()纳击,它不能傳入null原型來創(chuàng)建對象续扔,也不能接收第二個參數(shù)。
屬性相關(guān)操作
屬性繼承
通過繼承創(chuàng)建的新對象焕数,對新對象的操作并不會影響到原型對象纱昧。
var a = { r:1 };
var b = inherit(a);
b.x = 1; b.y = 1; // 新對象增加2個屬性
b.r = 2; // 修改新對象的繼承屬性
a.r; // => 1,原型對象的屬性不會受影響
假設(shè)要查詢對象o的屬性x堡赔,如果o中不存在x识脆,那么將會繼續(xù)在o的原型對象中查詢,直接找到x或者找到一個原型是null的對象為止加匈。<br/
屬性訪問錯誤
- 查詢一個不存在的屬性并不會報錯存璃。
book.subtitle; // => undefined:屬性不存在,但不會報錯
- 但是雕拼,如果對象不存在纵东,那么試圖查詢這個不存在對象的屬性就會報錯。
book.subtitle.length; // undefined沒有l(wèi)ength屬性啥寇,拋出一個類型錯誤異常
- 有一此屬性是只讀的偎球,不能重新賦值,但是設(shè)置屬性的失敗操作不會報錯辑甜。
Object.prototype = 0; // => Object原型是只讀的衰絮,賦值失敗,但不會報錯
下面提供兩種避免出錯的方法:
// 一種冗余但很易懂的方法
var len = undefined;
if(book) {
if(book.subtitle)
len = book.subtitle.length;
}
// 一種更簡練的方法磷醋,len被賦值為undefined或length
var len = book && book.subtitle && book.subtitle.length;
刪除屬性
- delete只是斷開屬性和宿主對象的聯(lián)系猫牡,而不會去操作屬性中的屬性
a = { p:{x:1} };
b = a.p;
delete a.p;
b.x; // => 1,已經(jīng)刪除的屬性引用(b)依然存在
- delete 只能刪除自有屬性邓线,不能刪除繼承屬性(要刪除繼承屬性必須從定義這個屬性的原型對象上刪除它淌友,而且這會影響所有繼承的對象)
- delete 不能刪除那么可配置性為false的屬性
var x = 1; // 使用var定義的全局變量
delete this.x; // 不能刪除,可配置性為false
this.y = 1; // 不使用var定義的全局變量
delete y; // 可刪除骇陈,可配置性為true
檢測屬性
可通過in運算符震庭、hasOwnProperty()和propertyIsEnumerable()來判斷某個屬性是否存在于某個對象中。
其中propertyIsEnumerable()是hasOwnProperty()的增強版你雌,保有檢測到是自有屬性并且這個屬性是可枚舉時才返回true器联。
var o = { x: 1 };
"x" in o; // true,x是o的屬性
"y" in o; // false,y不是o的屬性
"toString" in o; // true拨拓,【繼承屬性】(方法)
o.hasOwnProperty("x"); // true肴颊,x是【自有屬性】
o.hasOwnProperty("y"); // false
o.hasOwnProperty("toString"); // false,toString是【繼承屬性】
除了使用in運算符之外千元,還有更簡便的方法(!== undefined):
var o = { x: 1 };
o.x !== undefined; // true
o.y !== undefined; // false
o.toStirng !== undefined; // true
然而有一種場景這種方法是不可行的:屬性值被顯式地定義為undefined苫昌。
var o = { x: undefined };
o.x !== undefined; // false,屬性實際上是存在的P液!K钌怼!
注:還有一種情況是物独,這個屬性只有setter方法袜硫,而沒有g(shù)etter方法。
枚舉屬性
for/in循環(huán)可遍歷對象中所有可枚舉的屬性(包括自有屬性和繼承屬性)挡篓,對象的內(nèi)置方法是不可枚舉的婉陷,但在代碼中給對象添加的屬性是可枚舉的。
// 把p中的可枚舉屬性復(fù)制到o中官研,并返回o
// 如果o和p有同名屬性秽澳,則會覆蓋o中的屬性
// 這個函數(shù)不處理getter和setter以及復(fù)制屬性
function extend(o, p) {
// 遍歷p中所有可枚舉屬性
for(prop in p) {
o[prop] = p[prop];
}
return o;
}
存取器屬性(getter & setter)
在ECMAScript 5中,屬性值可以用一個或兩個方法替代戏羽,這兩個方法就是getter和setter担神。
- 和數(shù)據(jù)屬性不同,存取器屬性不具有可寫性始花。如果屬性同時具有g(shù)etter和setter方法角钩,那么它是一個讀/寫屬性郁副;如果只有g(shù)etter方法,則是一個只讀屬性嘲叔;如果只有setter方法恭朗,則是一個只寫屬性怔锌,讀取時返回undefined氯材。
- 和數(shù)據(jù)屬性一樣兆沙,存取器屬性是可以繼承的。
定義存取器屬性(使用直接量語法)的語法如下:
var p = {
// 普通的數(shù)據(jù)屬性
x: 1.0,
y: 1.0,
// 存取器屬性
get r() { return Match.sqrt(this.x*this.x + this.y*this.y); } , // 不需要function聲明男韧,并且在最后添加“,”
set r(newvalue) {
this.x = newvalue;
this.y = newvalue;
} ,
get theta() { return Match.atan2(this.y, this.x); }
}
代碼中定義了可讀寫屬性r和只讀屬性theta朴摊。
屬性的特性
除了包含名字和值之外,屬性還包含可寫煌抒、可枚舉和可配置的特性仍劈。
屬性類型 | 屬性特性 |
---|---|
數(shù)據(jù)屬性 | 值(value)厕倍、可寫性(writable)寡壮、可枚舉性(enumerable)、可配置性(configurable)。 |
存取器屬性 | 存取(get)况既、寫入(set)这溅、可枚舉性和可配置性。 |
可通過Object.getOwnPropertyDescriptor()獲取屬性描述符:
// 返回{value: 1, writable:true, enumerable:true, configurable:true}
Object.getOwnPropertyDescriptor({x:1}, "x");
var random = {
get octet() { return 1; }
}
// 返回{get:/*func*/, set:undefined, enumerable:true, configurable:true}
Object.getOwnPropertyDescriptor(random, "octet");
// 對于繼承屬性和不存在的屬性棒仍,返回undefined
Object.getOwnPropertyDescriptor({}, "x"); // undefined悲靴,沒有這個屬性
Object.getOwnPropertyDescriptor({}, "toStirng"); // undefined,繼承屬性
Object.defineProperty()用于設(shè)置屬性的特性:
var o = {}; // 創(chuàng)建一個空對象
// 添加一個屬性x莫其,并賦值為1
Object.defineProperty(o, "x", { value: 1,
writable: false,
enumerable: false,
configurable: true } );
o.x = 2; // 操作失敗癞尚,屬性不可寫
// 可通過重新設(shè)置屬性特性來改變屬性的值
Object.defineProperty(o, "x", {value: 2});
o.x; // => 2
// 將數(shù)據(jù)屬性修改存取器屬性
Object.defineProperty(o, "x", { get: function() { return 3; });
o.x; // => 3
前提 | 特性 |
---|---|
對象不可擴展 | 可以編輯已有屬性,但不能添加新屬性 |
屬性不可配置 | 不能修改它的可配置性和可枚舉性 |
存取器屬性不可配置 | 不能修改其getter和setter方法乱陡,也不能將它轉(zhuǎn)換為數(shù)據(jù)屬性 |
數(shù)據(jù)屬性不可配置 | 則不能將它轉(zhuǎn)換為存取器屬性浇揩;不能修改它的可寫性false=>true,但可以修改true=>false |
數(shù)據(jù)屬性不可配置且不可寫 | 不能修改屬性的值 |
數(shù)據(jù)屬性可配置不可寫 | 可以修改屬性的值(實際上先修改屬性為可寫憨颠,然后修改值) |
一個更完善的屬性擴展屬性函數(shù)extend():
Object.defineProperty(Object.prototype,
"extend",
{
writable: true,
enumerable: false,
configurable: true,
// 擴展值是一個函數(shù)
value: fucntion(o) {
// 得到所有的自有屬性胳徽,包括不可枚舉屬性
var names = Object.getOwnPropertyNames(o);
// 將屬性逐個擴展給Object.prototype
for(var i=0; i < names.length; i++) {
// 如果屬性已經(jīng)存在,則跳過
if(names[i] in this) continue;
var desc = Object.getOwnPropertyDescriptor(o, names[i]);
Object.defineProperty(this, names[i], desc);
}
}
}
);
對象的三個屬性
原型屬性
每一個JavaScript對象(null除外)都和另外一個對象相關(guān)聯(lián)爽彤,這個對象就是原型對象养盗,每一個對象都從原型繼承屬性。
沒有原型的對象為數(shù)不多适篙,Object.prototype就是其中之一往核。它不繼承任何屬性。
對象 | 原型 |
---|---|
通過對象直接量創(chuàng)建的對象 | Object.prototype匙瘪。 |
通過new創(chuàng)建的對象 | 構(gòu)造函數(shù)的prototype屬性铆铆。 |
通過Object.creat()創(chuàng)建的對象 | 第一個參數(shù)(也可以是null)作為它們的原型。 |
使用isPrototypeOf()進(jìn)行原型判斷丹喻,判斷在原型鏈(prototype chain)上進(jìn)行薄货,如:
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
類屬性
對象的類屬性(class attribute)是一個字符串谅猾,用以表示對象的類型信息。
默認(rèn)的toString()方法(繼承自O(shè)bject.prototype)返回了如下這種格式的字符串:
[object class]
很多對象繼承的toString()方法重寫了鳍悠,為了能調(diào)用正確的toString()方法税娜,必須間接地調(diào)用Function.call()方法。
function classof(o) {
if(o === null) return "Null";
if(o == undefined) return "Undefined";
return Object.prototype.toString.call(o).slice(8, -1); // 使用Function.call方法
}
通過對象直接量藏研、new構(gòu)造函數(shù)或者Object.create()這3種方式創(chuàng)建的對象敬矩,類屬性都是"Object"。
classof(null); // "Null"
classof(1); // "Number"
classof(""); // "String"
classof(false); // "Boolean"
classof({}); // "Object"
classof([]); // "Array"
classof(/./); // "RegExp"
classof(new Date());// "Date"
classof(window); // "Window"
function f() {}
classof(f); // "Function",注意與下面的區(qū)別
classof(new f()); // "Object"
可擴展性
對象的可擴展性表示是否可以給對象添加新屬性蠢挡。
所有的內(nèi)置對象和自定義對象都是顯式可擴展的弧岳,宿主對象的可擴展性由JavaScript引擎定義的凳忙。
方法 | 作用 |
---|---|
Object.isExtensible() | 判斷對象是否可擴展。 |
Object.preventExtensions() | 將對象設(shè)置為不可擴展禽炬。注意:一旦設(shè)置為不可擴展涧卵,將無法再轉(zhuǎn)換回可擴展。 |
Object.seal() | 將對象設(shè)置為不可擴展腹尖,同時所有的自有屬性不可配置柳恐。注意:對于已經(jīng)封閉(sealed)的對象不能解封。 |
Object.freeze() | 除了完成seal()的功能外热幔,同時還將所有的數(shù)據(jù)屬性設(shè)置為只讀(如果對象的存取器屬性具有settter方法乐设,存取器屬性將不受影響,仍可以通過給屬性賦值調(diào)用它們)绎巨。 |
序列化對象(serialization)
對象序列化(JSON.stringify())是指對象的狀態(tài)轉(zhuǎn)換為字符串伤提,也可將字符串還原(JSON.parse())為對象。
JSON.stringify()只能序列化對象可枚舉的自有屬性认烁。
o = { x:1, y:{z:[false, null, ""]} };
s = JSON.stringify(o); // s是"{ x:1, y:{z:[false, null, ""]} }"
p = JSON.parse(s); // p是o的深拷貝
- Number肿男、String、Boolean却嗡、null舶沛、對象、數(shù)組都可以序列化和還原窗价。NaN如庭、Infinity和-Infinity序列化結(jié)果是null。
- Date對象序列化的結(jié)果是ISO格式的日期字符串(參照Date.toJSON()函數(shù))撼港,但JSON.parse()依然保留它們的字符串形態(tài)坪它。
- 函數(shù)、RegExp帝牡、Error對象和undefined值不能序列化和還原(沒有定義toJSON()方法)往毡。
var obj = new Date();
var s = JSON.stringify(obj);
var d = JSON.parse(s);
alert(obj); // Sat May 14 2016 17:33:04 GMT+0800
alert(s); // "2016-05-14T09:33:04.874Z" (stringify返回時包含2層"")
alert(d); // 2016-05-14T09:33:04.874Z
alert(typeof d); // string類型,無法還原為Date類型
對象方法
toStirng()
由于默認(rèn)的toString()方法并不會輸出很多有用的信息([object Object])靶溜,因此很多類都帶有自定義的toString()开瞭。
例如,數(shù)組轉(zhuǎn)換為字符串時罩息,結(jié)果是一個數(shù)組元素列表嗤详;當(dāng)函數(shù)轉(zhuǎn)換為字符串時,得到函數(shù)的源代碼瓷炮。
toLocaleString()
這個方法返回本地化字符串葱色。Object默認(rèn)的toLocaleString()并不做任何本地化自身的操作,它僅調(diào)用toString()方法并返回對應(yīng)值娘香。
Date和Number類對toLocaleString()方法做了定制苍狰,可以用它對數(shù)字恐锣、日期和時間做本地化的轉(zhuǎn)換。
toJSON()
Object.property實際上沒有定義toJSON()方法舞痰,但對于需要序列化的對象來說,JSON.stringify()方法會調(diào)用toJSON()方法诀姚。
如果在待序列化的對象中存在這個方法响牛,則調(diào)用它,返回值即是序列化的結(jié)果赫段,而不是原始的對象呀打。
valueOf()
當(dāng)JavaScript需要將對象轉(zhuǎn)換為某種原始值時調(diào)用,尤其是轉(zhuǎn)換為數(shù)字的時候糯笙。