javascript中數據屬性和訪問器屬性
1.屬性
ECMAScript中有兩種屬性:數據屬性和訪問器屬性。
一、數據屬性
數據屬性包含一個數據值的位置,在這個位置可以讀取和寫入值。
var person = { name :"Nicholas" };
要修改屬性默認的特性,必須使用ECMAScript 5 的Object.defineProperty()方法,這個方法接收3個參數:屬性所在的對象,屬性的名字和一個描述符對象。其中,描述符(descriptor)對象的屬性必須是:configurable、enumerable、writable 和 value。設置其中的一或多個值,可以修改對應的特性值。例如:
var person = {};
Object.defineProperty(person, "name", {
writable: false,
value: "Nicholas"
});
alert(person.name); //"Nicholas"
person.name = "Greg";
alert(person.name); //"Nicholas"
這個例子創建了一個名為 name 的屬性,它的值"Nicholas"是只讀的。這個屬性的值是不可修改的,如果嘗試為它指定新值,則在非嚴格模式下,賦值操作將被忽略;在嚴格模式下,賦值操作將會導致拋出錯誤。
類似的規則也適用於不可配置的屬性。例如:
var person = {};
Object.defineProperty(person, "name", {
configurable: false,
value: "Nicholas"
});
alert(person.name); //"Nicholas"
delete person.name;
alert(person.name); //"Nicholas"
把 configurable 設置為 false,表示不能從對象中刪除屬性。如果對這個屬性調用 delete,則在非嚴格模式下什麽也不會發生,而在嚴格模式下會導致錯誤。而且,一旦把屬性定義為不可配置的,就不能再把它變回可配置了。此時,再調用 Object.defineProperty()方法修改除 writable 之外的特性,都會導致錯誤: var person = {}; Object.defineProperty(person, "name", {
configurable: false,
value: "Nicholas"
});
//拋出錯誤
Object.defineProperty(person, "name", {
configurable: true,
value: "Nicholas"
});
也就是說,可以多次調用 Object.defineProperty()方法修改同一個屬性,但在把 configurable
特性設置為 false 之後就會有限制了。
在調用 Object.defineProperty()方法時,如果不指定,configurable、enumerable 和writable 特性的默認值都是 false。多數情況下,可能都沒有必要利用 Object.defineProperty()方法提供的這些高級功能。不過,理解這些概念對理解 JavaScript 對象卻非常有用。
二、訪問器屬性
訪問器屬性不包含數據值;它們包含一對兒 getter 和 setter 函數(不過,這兩個函數都不是必需的)。
在讀取訪問器屬性時,會調用 getter 函數,這個函數負責返回有效的值;在寫入訪問器屬性時,會調用setter 函數並傳入新值,這個函數負責決定如何處理數據。訪問器屬性有如下 4 個特性。
? [[Configurable]]:表示能否通過 delete 刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為數據屬性。對於直接在對象上定義的屬性,這個特性的默認值為true。
? [[Enumerable]]:表示能否通過 for-in 循環返回屬性。對於直接在對象上定義的屬性,這個特性的默認值為 true。
? [[Get]]:在讀取屬性時調用的函數。默認值為 undefined。
? [[Set]]:在寫入屬性時調用的函數。默認值為 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 = 2005;
alert(book.edition); //2
以上代碼創建了一個 book 對象,並給它定義兩個默認的屬性:_year 和 edition。_year 前面的下劃線是一種常用的記號,用於表示只能通過對象方法訪問的屬性。而訪問器屬性 year 則包含一個getter 函數和一個 setter 函數。getter 函數返回_year 的值,setter 函數通過計算來確定正確的版本。因此,把 year 屬性修改為 2005 會導致_year 變成 2005,而 edition 變為 2。這是使用訪問器屬性的常見方式,即設置一個屬性的值會導致其他屬性發生變化。
不一定非要同時指定 getter 和 setter。只指定 getter 意味著屬性是不能寫,嘗試寫入屬性會被忽略。在嚴格模式下,嘗試寫入只指定了 getter 函數的屬性會拋出錯誤。類似地,只指定 setter 函數的屬性也不能讀,否則在非嚴格模式下會返回 undefined,而在嚴格模式下會拋出錯誤。
支持 ECMAScript 5 的這個方法的瀏覽器有 IE9+(IE8 只是部分實現)、Firefox 4+、Safari 5+、Opera 12+ 和 Chrome 。在這個方法之前,要創建訪問器屬性,一般都使用兩個非標準的方法:__defineGetter__()和__defineSetter__()。這兩個方法最初是由 Firefox 引入的,後來 Safari 3、Chrome 1 和 Opera 9.5 也給出了相同的實現。使用這兩個遺留的方法,可以像下面這樣重寫前面的例子。
var book = {
_year: 2004,
edition: 1
};
//定義訪問器的舊有方法
book.__defineGetter__("year", function(){
return this._year;
});
book.__defineSetter__("year", function(newValue){
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
});
book.year = 2005;
alert(book.edition); //2
在不支持Object.defineProperty()方法的瀏覽器中不能修改[[Configurable]]和[[Enumerable]]。
javascript中數據屬性和訪問器屬性