面向?qū)ο螅∣bject-Oriented,OO)的語言有一個(gè)標(biāo)志,那就是他們都有類的概念,而通過類可以創(chuàng)建任意多個(gè)具有相同屬性和方法的對(duì)象。
ECMAScript 中沒有類的概念,因此它的對(duì)象也與基于類的語言中的對(duì)象有所不同。
ECMA-262 把對(duì)象定義為:"無序?qū)傩缘募?,其屬性可以包含基本值、?duì)象或者函數(shù)。"嚴(yán)格來講,這就相當(dāng)于說對(duì)象是一組沒有特定順序的值。對(duì)象的每個(gè)屬性或方法都有一個(gè)名字,而每個(gè)名字都映射到一個(gè)值。我們可以把 ECMAScript 的對(duì)象想象成散列表:無非就是一組名值對(duì),其中值可以是數(shù)據(jù)或者函數(shù)。
每個(gè)對(duì)象都是基于一個(gè)引用類型創(chuàng)建的,這個(gè)引用類型可以是原生類型,也可以是開發(fā)人員定義的類型。
創(chuàng)建對(duì)象
// 使用 Object 創(chuàng)建對(duì)象
var person = new Object();
person.name = "Bert";
person.age = 24;
person.job = "IT";
person.sayName = function() {
alert(this.name);
}
// 通過對(duì)象字面量創(chuàng)建對(duì)象
var person {
name: "Bert";
age: 24;
job: "IT";
sayName: function() {
alert(this.name);
}
};
屬性類型
ECMA-262 第五版在定義只有內(nèi)部才用的特性(attribute)時(shí),描述了屬性(property)的各種特征。ECMA-262 定義這些特征是為了實(shí)現(xiàn) JavaScript 引擎用的,因此在 JavaScript 中不能直接訪問它們。為了表示特性是內(nèi)部值,該規(guī)范把它們放在了兩對(duì)方括號(hào)中。
例如:[[Enumerable]]
數(shù)據(jù)屬性
數(shù)據(jù)屬性包含一個(gè)數(shù)據(jù)值的位置。在這個(gè)位置可以讀取和寫入值。數(shù)據(jù)屬性有3個(gè)描述其行為的特性。
- [[Configurable]]:表示能否通過 delete 刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問屬器屬性。它的默認(rèn)值為 true。
- [[Enumerable]]:表示能否通過 for-in 循環(huán)返回屬性。它的默認(rèn)值為 true。
- [[Writable]]:表示能否修改屬性的值。它的默認(rèn)值為 true。
- [[Value]]:包含這個(gè)屬性的數(shù)據(jù)值。讀取屬性值的時(shí)候,從這個(gè)位置讀;寫入屬性值的時(shí)候,把新值保持在這個(gè)位置。它的默認(rèn)值為 undefined。
要修改屬性默認(rèn)的特性,必須使用 ECMAScript 5 的 Object.defineProperty() 方法,這個(gè)方法接收三個(gè)參數(shù):屬性所在的對(duì)象、屬性的名字和一個(gè)描述符對(duì)象。
其中,描述符(descriptor)對(duì)象的屬性必須是:Configurable、Enumerable、Writable 和 Value。
設(shè)置其中的一個(gè)或多個(gè),可以修改對(duì)應(yīng)的特性值:
var person = {};
Object.defineProperty(person, "name", {
writable: false,
value: "Bert"
})
alert(person.name); // Bert
person.name = "Greg";
alert(person.name); // Bert
當(dāng)屬性的 writable 值被設(shè)置為 false 時(shí),這個(gè)屬性的值是不可修改的,如果嘗試為它指定新值,則在非嚴(yán)格模式下,賦值操作將被忽略;在嚴(yán)格模式下,賦值操作將會(huì)導(dǎo)致拋出錯(cuò)誤。
var person = {};
Object.defineProperty(person, "name", {
configurable: false,
value: "Bert"
})
alert(person.name); // Bert
delete person.name;
alert(person.name); // Bert
把 configurable 設(shè)置為 false,表示不能從對(duì)象中刪除屬性。如果這個(gè)屬性調(diào)用 delete ,則在非嚴(yán)格模式下什么也不會(huì)發(fā)生,而在嚴(yán)格模式下會(huì)導(dǎo)致錯(cuò)誤。
一旦把屬性定義為不可配置的,就不能再把它變回可配置了。此時(shí),在調(diào)用 Object.defineProperty() 方法修改除 writable 之外的特性,都會(huì)導(dǎo)致錯(cuò)誤。
var person = {};
Object.defineProperty(person, "name", {
configurable: false,
value: "Bert"
});
// 拋出錯(cuò)誤:VM2473:8 Uncaught TypeError: Cannot redefine property: name(…)
Object.defineProperty(person, "name", {
configurable: true,
value: "Bert"
})
用 Object.defineProperty() 方法修改同一個(gè)屬性,但在把 configurable 特性設(shè)置為 false 之后就會(huì)有限制了。
在調(diào)用 Object.defineProperty() 方法時(shí),如果不指定,configurable、enumerable、和 writable 特性的默認(rèn)值都是false。多數(shù)情況下,可能都沒有必要利用 Object.defineProperty() 方法提供的這些高級(jí)功能。
IE8 是第一個(gè)實(shí)現(xiàn) Object.defineProperty() 方法的瀏覽器版本。然而,這個(gè)版本的實(shí)現(xiàn)存在諸多限制:只能在DOM對(duì)象上使用這個(gè)方法,而且只能創(chuàng)建訪問器屬性。由于實(shí)現(xiàn)不徹底,建議不要在 IE8 中使用 Object.defineProperty() 方法。
訪問屬性
訪問器屬性不包含數(shù)據(jù)值;它們包含一對(duì) getter 和 setter 函數(shù)(不過,這兩個(gè)函數(shù)都不是必須的)。在讀取訪問器屬性時(shí),會(huì)調(diào)用 getter 函數(shù),這個(gè)函數(shù)負(fù)責(zé)返回有效的值;在寫入訪問器屬性時(shí),會(huì)調(diào)用 setter 函數(shù)并傳入新值,這個(gè)函數(shù)負(fù)責(zé)決定如何處理數(shù)據(jù)。
訪問函數(shù)的4個(gè)特性:
- [[Configurable]]:表示是否通過 delete 刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為數(shù)據(jù)屬性。對(duì)于直接在對(duì)象上定義的屬性,這個(gè)特性默認(rèn)值為 true。
- [[Enumerable]]:表示能否通過 for-in 循環(huán)返回屬性。對(duì)于直接在對(duì)象上定義的屬性,這個(gè)特性的默認(rèn)值為 true。
- [[Get]]:在讀取屬性時(shí)調(diào)用的函數(shù)。默認(rèn)值為 undefined。
- [[Set]]:在寫入屬性時(shí)調(diào)用的函數(shù)。默認(rèn)值為 undefined。
訪問屬性不能直接定義,必須使用 Object.defineProperty() 來定義:
var book = {
_year: 2004,
edition: 1
}
Object.defineProperty(book, "year", {
get: function() {
return this._year;
},
set: function(newValue) {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
})
book.year = 2006;
alert(book.edition); // 3
上例是訪問器屬性的常見方式,即設(shè)置一個(gè)屬性的值會(huì)導(dǎo)致其他屬性發(fā)生變化。
不一定非要同時(shí)制定 getter 和 setter。 只指定 getter 意味著屬性是不能寫,嘗試寫入屬性會(huì)被忽略。在嚴(yán)格模式下,嘗試寫入指定了 getter 函數(shù)的屬性會(huì)拋出錯(cuò)誤。
沒有指定 setter 函數(shù)的屬性不能讀,在非嚴(yán)格模式下會(huì)返回 undefined,而在嚴(yán)格模式下會(huì)拋出錯(cuò)誤。
支持 ECMAScript 5 的這個(gè)方法的瀏覽器有 IE9+(IE8只是部分實(shí)現(xiàn))、Firefox 4+、Safari 5+、Opera 12+ 和 Chrome。在這個(gè)方法之前,要?jiǎng)?chuàng)建訪問器屬性,一般都使用兩個(gè)非標(biāo)準(zhǔn)方法: __defineGetter__() 和 __defineSetter__()。這兩個(gè)方法最初是由 Firefox 引入的,后來 Safari 3、Chrome 1 和 Opera 9.5 也給出了相同的實(shí)現(xiàn)。
var book = {
_year: 2004,
edition: 1
}
// 定義訪問方法
book.__defineGetter__("year", {
return this._year;
}
book.__defineSetter__("year", {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
book.year = 2006;
alert(book.edition); // 3
不支持 Object.defineProperty() 方法的瀏覽器中不能修改 [[Configurable]] 和 [[Enumerable]];
定義多個(gè)屬性
由于為對(duì)象定義多個(gè)屬性的可能性很大,ECMAScript 5 又定義了一個(gè) Object.defineProperties() 方法。利用這個(gè)犯法可以通過描述符一次定義多個(gè)屬性。
這個(gè)方法接收兩個(gè)對(duì)象參數(shù):第一個(gè)對(duì)象是要添加和修改其屬性的對(duì)象,第二個(gè)對(duì)象的屬性與第一個(gè)對(duì)象中要添加或修改的屬性一一對(duì)應(yīng)。
var book ={};
Object.defineProperties(book, {
_year: {
value: 2004
},
edition: {
value: 1
},
year: {
get: function() {
return this.year;
},
set: function(newValue) {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
}
})
支持Object.defineProperties() 方法的瀏覽器有 IE9、Firefox 4+、Safari 5+、Opera 12+ 和 Chrome
讀取屬性的特性
使用 ECMAScript 5 的 Object.getOwnPropertyDescriptor() 方法,可以取得給定屬性的描述符。
這個(gè)方法接收兩個(gè)參數(shù):屬性所在的對(duì)象和要讀取其描述符的屬性名稱。返回值是一個(gè)對(duì)象,如果是訪問器屬性,這個(gè)對(duì)象屬性有 configurable、enumerable、get 和 set;如果是數(shù)據(jù)屬性,這個(gè)對(duì)象的屬性有 configurable、enumerable、writable。
var book = {};
Object.defineProperties(book, {
__year: {
value: 2004
},
edition: {
value: 1
},
year: {
get: function() {
return this.year;
},
set: function(newValue) {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
}
})
var description = Object.getOwnPropertyDescriptor(book, "__year");
alert(descriptor.value); // 2004
alert(descriptor.configurable); // false
alert(descriptor.get); // undefined
var description = Object.getOwnPropertyDescriptor(book, "year");
alert(descriptor.value); // undefined
alert(descriptor.enumerable); // false
alert(descriptor.get); // function
對(duì)于數(shù)據(jù)屬性 __year,value 等于最初的值,configurable 是 false,而 get 等于 undefined 。對(duì)于訪問器屬性 year , value 等于 undefined , enumerable 是 false ,而 get 是一個(gè)指向 getter 函數(shù)的指針。
在 javascript 中, 可以針對(duì)任何對(duì)象 ---- 包括 DOM 和 BOM 對(duì)象,使用 Object.getOwnPropertyDescriptor() 方法。支持這個(gè)方法的瀏覽器有 IE9、Firefox 4+、Safari 5+、Opera 12+ 和 Chrome。