Object函式的內建方法 Object.defineProperty
一、定義
Object.defineProperty()方法用於在物件上定義或修改一個自有屬性,並返回這個物件。
二、語法
Object.defineProperty(obj, prop, descriptor)
引數:
obj:要定義或修改屬性的物件
prop:屬性字串名
descriptor:屬性描述符物件
返回值:
被傳遞給函式的物件
三、屬性描述符
物件的屬性的屬性描述符主要有兩種形式,分別是資料描述符和存取描述符。
資料描述符是一個具有值的屬性。
存取描述符是由getter和setter函式描述的屬性。
資料描述符具有的鍵值:
configurable:可配置性。當該特性為true時,屬性描述符可以被修改,屬性可以被刪除。預設為false。
enumerable:可列舉性。當該特性為true時,屬性可以出現在物件的列舉屬性中。預設為false。
writable:可寫性。當該特性為true時,屬性的值可以被賦值運算子改變。預設為false。
value:屬性對應的值。可以是任意有效的JavaScript值。預設為undefined。
存取描述符具有的鍵值:
configurable:可配置性。當該特性為true時,屬性描述符可以被修改,屬性可以被刪除。預設為false。
enumerable:可列舉性。當該特性為true時,屬性可以出現在物件的列舉屬性中。預設為false。
get:一個給屬性提供getter的方法。如果沒有getter則預設為undefined,屬性將不可讀。
set:一個給屬性提供setter的方法。如果沒有setter則預設為undefined,屬性將不可以修改。
如果一個屬性描述符不具有value、writable、set和get的任意一個關鍵字,則認為是一個數據描述符。
四、示例
示例1:定義一個數據屬性
var obj = Object.defineProperty({}, 'a', { value: 1 }); Object.getOwnPropertyDescriptor(obj, 'a'); // {value: 1, writable: false, enumerable: false, configurable: false} obj.b = 2; Object.getOwnPropertyDescriptor(obj, 'b'); // {value: 2, writable: true, enumerable: true, configurable: true}
通過Object.defineProperty()方法定義的資料屬性,如果沒有指定writable、enumerable、configurable特性,則這三個特性預設為false。
通過表示式直接建立的屬性(b),預設是資料屬性,且writable、enumerable、configurable特性,則這三個特性預設為true。
示例2:定義一個存取屬性
var obj = { x: 1 };
Object.defineProperty(obj, 'a', {
get: function () { return this.x;},
set: function (val) { this.x = val;}
});
Object.getOwnPropertyDescriptor(obj, 'a');
// {get: ƒ, set: ƒ, enumerable: false, configurable: false}
通過Object.defineProperty()方法定義的存取屬性,如果沒有指定enumerable、configurable特性,則這兩個特性預設為false。
示例3:資料屬性的特性功能示例
// 建立一個空物件
var obj = {};
// 定義一個可寫的、不可列舉的、可配置的屬性x
Object.defineProperty(obj, 'x', {
value: 1,
writable: true,
enumerable: false,
configurable: true
});
// 訪問物件的屬性
obj.x; // 1,說明屬性是存在的
// 列舉物件的屬性,獲取不到屬性x
Object.keys(obj); // []
// 修改屬性的可寫性,使其變為只讀
Object.defineProperty(obj, 'x', { writable: false });
// 嘗試修改屬性的值
obj.x = 2;
obj.x; // 1, 靜默修改失敗,在嚴格模式下引發TypeError異常。
// 目前屬性是可配置的,故可以通過defineProperty來修改屬性的值
Object.defineProperty(obj, 'x', { value: 2 });
obj.x; // 2 屬性值修改成功
// 將屬性可寫性改回可寫
Object.defineProperty(obj, 'x', { writable: true });
// 將屬性可配置性改成不可配置
Object.defineProperty(obj, 'x', { configurable: false });
// 此時,可以修改物件屬性的值,但不能修改物件屬性的可列舉性和可配置性,強行修改會引發TypeError
// 但此時,依然可以把物件屬性的可寫性改回不可寫
Object.defineProperty(obj, 'x', { writable: false });
// 自從之後,物件的屬性x,不可配置、不可修改、不可列舉、不可刪除。
如果configurable為false,不可修改enumerable和configurable,但可以把writable從true改為false,無法把writable從false改為true。
如果configurable為true,writable為false,只能通過Object.defineProperty()方法來修改屬性的值。
示例4:存取屬性的特性功能演示
var obj = {};
obj.a = 1;
obj.b = 2;
Object.defineProperty(obj, 'c', {
get: function () {
return Math.sqrt(obj.a * obj.a + obj.b * obj.b);
},
set: function (value) {
obj.a = value;
}
});
Object.getOwnPropertyDescriptor(obj, 'c');
// {get: ƒ, set: ƒ, enumerable: false, configurable: false}
// 由於在定義屬性c的時候沒有顯式指定enumerable和configurable的值,故這2個特性預設為false。
// 此時不能修改其getter和setter方法。
// 嘗試修改為資料屬性
Object.defineProperty(obj, 'c', { value: 3 });
// 上述操作會引發TypeError:Cannot redefine property: c
如果存取屬性是不可配置的,則不能修改其getter和setter方法,也不能將其轉換為資料屬性。
示例5:資料屬性和存取屬性的轉換
var obj = {};
// 定義一個可寫、可配置、可列舉的資料屬性
Object.defineProperty(obj, 'a', {
value: 1,
writable: true,
enumerable: true,
configurable: true
});
Object.getOwnPropertyDescriptor(obj, 'a');
// {value: 1, writable: true, enumerable: true, configurable: true}
obj.a; // 1
// 轉換為存取屬性
Object.defineProperty(obj, 'a', {
get: function () {
console.log('get a');
return 2;
}
});
Object.getOwnPropertyDescriptor(obj, 'a');
// {get: ƒ, set: undefined, enumerable: true, configurable: true}
obj.a; // 'get a', 2
// 再轉回資料屬性
Object.defineProperty(obj, 'a', {
value: 3
});
obj.a; // 3
如果屬性是可配置的,則可以從資料屬性轉為存取屬性,也可以從存取屬性轉為資料屬性。