屬性類型
JS中有兩種屬性:數據屬性和訪問器屬性。
數據屬性
數據屬性包含一個數據值的位置;在這個位置可以讀取和寫入值。
數據屬性具有4個描述其行為的特性:
[[Configurable]]
表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性,能否把屬性修改為訪問器屬性。
直接在對象上定義的屬性,默認為true。
[[Enumerable]]
表示能否通過for-in循環返回屬性。
直接在對象上定義的屬性,默認為true。
除了for-in循環之外,ECMAScript5定義了兩個用以枚舉屬性名稱的函數。
Object.keys():返回一個數組,這個數組由對象中可枚舉的自有屬性的名稱組成。
其工作原理類似這樣:
1 function keys(object) { 2 if(typeof object !== "object") { 3 throw TypeError; //參數必須是對象 4 }; 5 var result = []; //創建一個數組,用以保存枚舉的屬性 6 for (prop in object) { //遍歷所有可枚舉的屬性 7 if (object.hasOwnProperty(prop)) { //判斷是否為自有屬性 8 result.push(prop); //將屬性名添加到數組 9 }; 10 }; 11 return result; //返回數組 12 };
Object.getOwnPropertyNames():返回對象的所有自有屬性的名稱,而不僅限於可枚舉的屬性。
[[Writable]]
表示能否修改屬性的值。
直接在對象上定義的屬性,默認為true。
[[Value]]
包含這個屬性的數據值;讀取屬性值的時候,從這個位置讀取;寫入屬性值的時候,把值保存在這里。
這個特性默認為undefined。
訪問器屬性
訪問器屬性包含一對getter和setter方法。
如果屬性同時具有getter和setter方法,那么它就是一個讀/寫屬性;如果只有getter方法,則為只讀屬性;同理,如果只有setter方法,則為只寫屬性。
訪問器屬性有如下4個特性:
[[Configurable]]
表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性,能否把屬性修改為數據屬性。
直接在對象上定義的屬性,默認為true。
[[Enumerable]]
表示能否通過for-in循環返回屬性。
直接在對象上定義的屬性,默認為true。
[[Get]]
在讀取屬性時調用的函數;默認值為undefined。
在讀取訪問器屬性時,會調用gette方法,這個函數負責返回有效的值。
[[Set]]
在寫入屬性時調用的函數;默認值為undefined。
在寫入訪問器屬性時,會調用setter方法並傳入新值,這個函數負責決定如何處理數據。
訪問器屬性不能直接定義,必須使用Object.defineProperties()方法來定義。
定義多個屬性
要修改數據屬性默認的特性,必須使用ECMAScript5中的Object.defineProperty()方法;接受三個參數:屬性所在的對象、屬性的名字、描述符對象;其中,數據屬性描述符對象的屬性必須是:configurable、enumerable、writable、value;訪問器屬性的描述符對象的屬性有configurable、enumerable、get、set。設置其中的一個或多個值,可以修改對應的特性值。
1 var person = {}; 2 Object.defineProperty(person, "name", { 3 writable: false, 4 value: "CC" 5 }); 6 alert(person.name); //"CC" 7 person.name = "VV"; 8 alert(person.name); //"CC"
在調用Object.defineProperty()方法新建屬性時,如果不指定,configurable、enumerable、writable特性的值都是false。
注意:使用Object.defineProperty()方法,要么修改已有屬性,要么新建自有屬性,不能修改繼承屬性。
Object.defineProperties()方法,通過描述符一次性定義多個屬性;
接收兩個對象參數:第一個對象是要添加或修改其屬性的對象;第二個對象的屬性與第一個對象中要添加或修改的屬性一一對應。
1 var person = {}; 2 Object.defineProperties(person, { 3 name: { 4 value: "CC", 5 writable: true 6 }, 7 age: { 8 value: 23, 9 writable: false 10 } 11 }); 12 alert(person.name); //"CC" 13 person.name = "VV"; 14 alert(person.name); //"VV"
讀取屬性的特性
Object.getOwnPropertyDescriptor()方法,可以取得給定屬性的描述符;
接收兩個參數:屬性所在的對象,要讀取其描述符的屬性名稱。
返回值是一個對象;如果是數據屬性,這個對象的屬性有configurable、enumerable、writable、value;如果是訪問器屬性,這個對象的屬性有configurable、enumerable、get、set。
1 var person = {}; 2 Object.defineProperties(person, { 3 name: { 4 value: "CC", 5 writable: true 6 }, 7 age: { 8 value: 23, 9 writable: false 10 } 11 }); 12 var descriptor = Object.getOwnPropertyDescriptor(person, "name"); 13 alert(descriptor.value); //"CC" 14 alert(descriptor.writable); //true
注意:
Object.getOwnPropertyDescriptor()方法,只能用於實例屬性;如果要取得原型屬性的描述符,必須直接在原型對象上調用Object.getOwnPropertyDescriptor()方法。
《JS權威指南》——Object.getOwnPropertyDescriptor()方法只能取得自有屬性的描述符,要想獲得繼承的屬性,必須遍歷原型鏈。
屬性的特性規則
如果對象是不可擴展的,則可以編輯已有的自有屬性,但不能添加新屬性;
如果屬性是不可配置的,則不能修改它的可配置性和可枚舉性(一旦將confidurable特性改為false,不能再改回true);
如果訪問器屬性是不可配置的,則不能修改其getter和setter方法,也不能將其轉換為數據類型;
如果數據屬性是不可配置的,則不能將它的writable特性從false改為true,但可以從true改為false;也不能將其轉換為訪問器屬性;
如果數據屬性是不可配置且不可寫的,則不能修改它的value值;然而可配置但不可寫的value值是可以修改的(實際上是先標記為可寫的,然后修改value值,最后轉換為不可寫的)。